C#枚举器和迭代器
使用foreach语句时,可以依次取出数组里面的元素,原因就是数组提供了“枚举器(Enumerator)”,枚举器知道元素的位置并返回请求项。
枚举器IEnumerator
枚举器实现了IEnumerator
接口,该接口中有Current属性、MoveNext和Reset方法,foreach实现原理类似如下代码:
1 2 3 4 5 6 7 8 9 10 11
| static void Main(string[] args) { int[] MyArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; IEnumerator ie = MyArray.GetEnumerator(); while ( ie.MoveNext()) { int i = (int)ie.Current; Console.WriteLine(i); } }
|
示例
- 定义一个可枚举类
1 2 3 4 5 6 7 8 9 10
| class Colors : IEnumerable { string[] colors = { "blue", "red", "yellow" };
public IEnumerator GetEnumerator() { return new ColorEnumerator(colors); } }
|
- 定义可枚举类中使用到的枚举器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class ColorEnumerator : IEnumerator { string[] colors; int positon = -1; public ColorEnumerator(string[] colors) { this.colors = colors; for (int i = 0; i < colors.Length; i++) { this.colors[i] = colors[i]; } }
public object Current { get { if (positon == -1 || positon >= colors.Length) throw new InvalidOperationException(); return colors[positon]; } }
public bool MoveNext() { if(positon <colors.Length-1) { positon++; return true; } else { return false; } }
public void Reset() { positon = -1; } }
|
- 使用可枚举类
1 2 3 4 5 6 7 8 9
| static void Main(string[] args) { Colors colors = new Colors(); foreach (var item in colors) { Console.WriteLine(item); } Console.Read(); }
|

泛型枚举器
IEnumerator<T>
泛型枚举器与普通枚举器类似,不同之处在于普通枚举器的Current
属性是Object类型,在取出是需要进行转化(看上面while代码块中的代码),而泛型可以直接返回指定的类型。
迭代器
迭代器使用
迭代器简化了可枚举器和可枚举类型的编码工作。yield return
表示依次返回枚举中的下一项。
如何要实现上面的实例,只需要实现可枚举类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Colors { string[] colors = { "blue", "red", "yellow" };
public IEnumerator GetEnumerator() { return GetColorsEnumerator(); } public IEnumerator<string> GetColorsEnumerator() { yield return "blue"; yield return "red"; yield return "yellow"; } }
|
结果与上面示例完全相同
常见迭代器模式
- 迭代器返回枚举器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Colors { public IEnumerator GetEnumerator() { return GetColors(); } public IEnumerator<string> GetColors() { yield return ..; yield return ..; ... } }
|
1 2 3 4 5
| Colors colors = new Colors(); foreach (var item in colors) { Console.WriteLine(item); }
|
- 迭代器返回可枚举类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Colors { public IEnumerator GetEnumerator() { return GetColors().GetEnumerator(); } public IEnumerable<string> GetColors() { yield return ..; yield return ..; ... } }
|
1 2 3 4 5 6 7 8 9 10 11
| Colors colors = new Colors();
foreach (var item in colors) { Console.WriteLine(item); }
foreach (var item in colors.GetColors()) { Console.WriteLine(item); }
|
多个迭代器
上面讲的迭代器有两种模式,为什么需要第2种方式?答案时候第2中方式更加灵活,比如可以实现多个迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Colors { string[] colors = { "blue", "red", "yellow" };
public IEnumerable<string> A() { for (int i = 0; i < colors.Length; i++) { yield return colors[i]; } }
public IEnumerable<string> B() { for (int i = colors.Length-1; i >=0; i--) { yield return colors[i]; } } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static void Main(string[] args) { Colors colors = new Colors(); foreach (var item in colors.A()) { Console.WriteLine(item); } Console.WriteLine("--------------"); foreach (var item in colors.B()) { Console.WriteLine(item); } Console.Read(); }
|

迭代器作为属性
上面的迭代器是作为方法,可以将方法封装为属性,方便调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Colors { string[] colors = { "blue", "red", "yellow" };
public IEnumerable<string> A { get { for (int i = 0; i < colors.Length; i++) { yield return colors[i]; } } }
public IEnumerable<string> B { get { for (int i = colors.Length - 1; i >= 0; i--) { yield return colors[i]; } } } }
|
使用方式基本相同,只不过是将方法调用改成了属性