17. 迭代器模式(Iterator Pattern)
分类: 行为型模式
热门度: ★★★★★
难度: ★★☆☆☆
📖 概念
迭代器模式提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。C# 中IEnumerable<T>和IEnumerator<T>就是迭代器模式的标准实现。
🎯 意图
提供统一的遍历接口,隐藏集合的内部结构,支持多种遍历方式。
🔑 关键角色
| 角色 | 说明 |
|---|---|
| IIterator(迭代器接口) | 定义 HasNext()、Next() 等遍历方法 |
| ConcreteIterator(具体迭代器) | 实现遍历逻辑,维护当前位置 |
| IAggregate(聚合接口) | 定义创建迭代器的方法 |
| ConcreteAggregate(具体聚合) | 实现聚合接口,提供数据源 |
⚠️ 注意事项
- C# 内置了
IEnumerable<T>/IEnumerator<T>,通常直接实现即可 - 注意迭代过程中的集合修改问题(
InvalidOperationException) - 可用
yield return简化迭代器实现
🔄 实现流程
1. 实现 IEnumerable<T> 接口(GetEnumerator 方法) 2. 实现 IEnumerator<T> 接口(MoveNext、Current、Reset) 3. 或使用 yield return 语法糖自动实现 4. 客户端通过 foreach 遍历💡 常见使用场景
场景1: 自定义集合遍历
// 自定义集合publicclassBookCollection:IEnumerable<Book>{privateList<Book>_books=new();publicvoidAdd(Bookbook)=>_books.Add(book);publicIEnumerator<Book>GetEnumerator()=>newBookEnumerator(_books);System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator()=>GetEnumerator();}publicclassBook{publicstringTitle{get;set;}publicstringAuthor{get;set;}publicBook(stringtitle,stringauthor){Title=title;Author=author;}}// 自定义迭代器publicclassBookEnumerator:IEnumerator<Book>{privateList<Book>_books;privateint_position=-1;publicBookEnumerator(List<Book>books){_books=books;}publicBookCurrent=>_books[_position];objectSystem.Collections.IEnumerator.Current=>Current;publicboolMoveNext()=>++_position<_books.Count;publicvoidReset()=>_position=-1;publicvoidDispose(){}}// 使用varcollection=newBookCollection();collection.Add(newBook("设计模式","GoF"));collection.Add(newBook("重构","Martin Fowler"));collection.Add(newBook("代码整洁之道","Robert C. Martin"));foreach(varbookincollection)Console.WriteLine($"{book.Title}-{book.Author}");场景2: 二叉树中序遍历迭代器
publicclassTreeNode<T>{publicTValue{get;set;}publicTreeNode<T>Left{get;set;}publicTreeNode<T>Right{get;set;}publicTreeNode(Tvalue){Value=value;}}publicclassInOrderEnumerator<T>:IEnumerator<T>{privateStack<TreeNode<T>>_stack=new();privateTreeNode<T>_current;privateTreeNode<T>_root;publicInOrderEnumerator(TreeNode<T>root){_root=root;_current=root;PushLeft(root);}privatevoidPushLeft(TreeNode<T>node){while(node!=null){_stack.Push(node);node=node.Left;}}publicTCurrent=>_current.Value;objectSystem.Collections.IEnumerator.Current=>Current;publicboolMoveNext(){if(_stack.Count==0)returnfalse;_current=_stack.Pop();if(_current.Right!=null)PushLeft(_current.Right);returntrue;}publicvoidReset(){_stack.Clear();PushLeft(_root);}publicvoidDispose(){}}publicclassBinaryTree<T>:IEnumerable<T>{publicTreeNode<T>Root{get;set;}publicBinaryTree(TreeNode<T>root){Root=root;}publicIEnumerator<T>GetEnumerator()=>newInOrderEnumerator<T>(Root);System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator()=>GetEnumerator();}// 使用vartree=newBinaryTree<int>(newTreeNode<int>(4){Left=newTreeNode<int>(2){Left=newTreeNode<int>(1),Right=newTreeNode<int>(3)},Right=newTreeNode<int>(6){Left=newTreeNode<int>(5),Right=newTreeNode<int>(7)}});foreach(varvalintree)Console.Write($"{val}");// 1 2 3 4 5 6 7场景3: 使用 yield return 简化实现
publicclassRange{privateint_start,_end,_step;publicRange(intstart,intend,intstep=1){_start=start;_end=end;_step=step;}// 使用 yield return 自动生成迭代器publicIEnumerable<int>GetNumbers(){for(inti=_start;i<_end;i+=_step)yieldreturni;}// 斐波那契无限序列publicstaticIEnumerable<int>Fibonacci(){inta=0,b=1;while(true){yieldreturna;(a,b)=(b,a+b);}}// 按条件过滤publicIEnumerable<T>Filter<T>(IEnumerable<T>source,Func<T,bool>predicate){foreach(variteminsource)if(predicate(item))yieldreturnitem;}}// 使用foreach(varninnewRange(1,10,2).GetNumbers())Console.Write($"{n}");// 1 3 5 7 9foreach(varfibinRange.Fibonacci().Take(10))Console.Write($"{fib}");// 0 1 1 2 3 5 8 13 21 34✅ 优点
- 支持多种遍历方式
- 简化聚合类,不需要暴露内部结构
- 可以同时对同一聚合进行多次遍历
❌ 缺点
- 增加了类的数量
- 遍历比直接访问元素略慢
📊 与其他模式对比
| 模式 | 区别 |
|---|---|
| 组合模式 | 组合模式管理树结构,迭代器模式遍历元素 |
| 访问者模式 | 访问者在元素上执行操作,迭代器只负责遍历 |
| 备忘录模式 | 备忘录保存状态,迭代器遍历集合 |