出处:https://www.cnblogs.com/wyy1234/
1.迭代器模式介绍
迭代器模式主要用于遍历聚合对象,将聚合对象的遍历行为分离出来,抽象为一个迭代器来负责。迭代器模式用的十分普遍,C#/JAVA等高级语言都对迭代器进行了封装用于遍历数组,集合,列表等,因为各种高级语言都对这种模式做了很好的封装,所以这种模式的使用价值远大于它的学习价值,MartinFlower甚至在网站上提出过撤销这个设计模式,所以这里不打算介绍迭代器模式的概念和原理,而是介绍C#中的迭代器模式应用:C#中的枚举器和迭代器。
2、枚举器和可枚举类型
先看一个简单的例子:
1 static void Main(string[] args) 2 { 3 int[] arr = { 2,3,5,8}; 4 foreach (int item in arr) 5 { 6 Console.WriteLine("item‘s Value is :{0}",item); 7 } 8 Console.ReadKey(); 9 }
为什么数组能够通过foreach遍历呢?原因是数组实现了IEnumerable接口,IEumerable接口中只有一个成员方法:GetEnumerator(),这个方法返回一个枚举器对象,这个枚举器就是迭代器模式中的迭代器。枚举器可以依次返回请求中数组中的元素,它知道元素的顺序并跟踪元素在序列中的位置,然后返回请求的当前项,我们可以通过GetEnumerator方法获取枚举器对象。那么什么是枚举器呢?实现IEnumerator接口的类型就是枚举器,该接口有三个成员:
current :获取当前位置元素
MoveNext() :把枚举器位置前进到下一项,返回bool,表示位置是否有效(如果没有下一项返回false)
Reset() :把位置重置为原始状态的位置(有索引是一般为-1)
我们重新实现上边例子的foreach操作:
1 static void Main(string[] args) 2 { 3 int[] arr = { 2,3,5,8}; 4 //获取arr的枚举器 5 IEnumerator ie = arr.GetEnumerator(); 6 while (ie.MoveNext()) 7 { 8 int i = (int)ie.Current; 9 Console.WriteLine("item‘s value is:{0}",i); 10 } 11 Console.ReadKey(); 12 }
知道了foreach内部是怎么运行的后,我们就可以自己实现一个可以用foreach遍历的类了:自定义的类要实现IEnumerable接口的GetEnumerator方法,这个方法返回一个枚举器(就是一个继承IEnumerator接口的类型),以遍历自定义颜色集合为例,代码如下:
class Program { static void Main(string[] args) { ColorList colors = new ColorList(); //foreach遍历自定义的类型 foreach (var item in colors) { Console.WriteLine(item); } Console.ReadKey(); } /// <summary> /// 自定义类 ColorList实现IEnumerable接口 /// </summary> public class ColorList : IEnumerable { //实现GetEnumerator接口方法 public IEnumerator GetEnumerator() { return new ColorEnumrator(new string[]{ "red", "blue", "green", "pink" }); } } /// <summary> /// 自定义枚举器 /// </summary> public class ColorEnumrator : IEnumerator { string[] _colors; //位置索引 private int _position = -1; //枚举器构造方法 public ColorEnumrator(string[] theColors) { _colors = new string[theColors.Length]; for (int i = 0; i < theColors.Length; i++) { _colors[i] = theColors[i]; } } //获取当前项的值 public object Current { get { if (_position < 0 || _position > _colors.Length) { throw new Exception("超过边界了!"); } return _colors[_position]; } } //指向下一项 public bool MoveNext() { if (_position<_colors.Length-1) { _position++; return true; } return false; } //复位 public void Reset() { _position = -1; } } }
程序运行结果如下:
总结:可枚举类型是实现了IEnumerable接口的类,IEnumerable接口只有一个成员GetEnumerator方法,用于获取枚举器(实现IEnumerator接口的实例)。
3.迭代器
通过手动实现IEnumerable接口的IEnumerator方法我们已经可以实现自定义可枚举类型了,在C#2.0中提供了更简单的创建可枚举类型的方式:迭代器。我们使用迭代器时,不用我们自己去手动创建IEnumerator实例,编译器会自动帮我们生成IEnumerator内部的Current,MoveNext和Reset方法。使用迭代器上边的例子可以简化为:
class Program { static void Main(string[] args) { ColorList colors = new ColorList(); //foreach遍历自定义的ColorList类型 foreach (var item in colors) { Console.WriteLine(item); } Console.ReadKey(); } } /// <summary> /// 自定义颜色集合,实现IEnumerable接口 /// </summary> public class ColorList : IEnumerable { //实现GetEnumerator接口方法 public IEnumerator GetEnumerator() { string[] colors= { "red", "green", "blue", "pink" }; for (int i = 0; i < colors.Length; i++) { //yield return的作用是指定下一项的内容 yield return colors[i]; } //想反向遍历时可以这样写 //for (int i = colors.Length-1; i >=0; i--) //{ // yield return colors[i]; //} } }
程序运行结果和手动写Enumerator的例子一致,我们可以看出使用迭代器来生成可枚举类型要简单很多,这并不是创建迭代器的过程简单了,而是微软让编译器帮我们自动生成了IEnumerator中的current,MoveNext(),Reset()等内容。