一 枚舉器和可枚舉類型 當我們為數組使用foreach語句時,這個語句為我們依次取出了數組中的每一個元素。 var arrInt = new int[] { 11, 12, 13, 14 }; foreach (var item in arrInt) { Console.WriteLine(item ...
一 枚舉器和可枚舉類型
當我們為數組使用foreach語句時,這個語句為我們依次取出了數組中的每一個元素。
var arrInt = new int[] { 11, 12, 13, 14 }; foreach (var item in arrInt) { Console.WriteLine(item); }
原因是數組實現了IEnumerable介面,介面提供了一個GetEnumerator方法可以獲取一個實現了IEnumerator介面的枚舉器對象。
枚舉器可以依次返回請求的數組中的元素。
實現了IEnumerable介面類型叫做可枚舉類型。數組是可枚舉類型。
public interface IEnumerable { // 摘要: 返回迴圈訪問集合的枚舉器。 // 返回結果:一個可用於迴圈訪問集合的 System.Collections.IEnumerator 對象。 IEnumerator GetEnumerator(); }
foreach語句設計用來和可枚舉類型一起使用。只要給它的遍歷對象是可枚舉類型,它就會執行如下行為:
- 通過調用GetEnumerator方法獲取對象的枚舉器;
- 從枚舉器中請求每一項並把它作為迭代變數。
二 IEnumerator介面
IEnumerator介面的定義如下:
public interface IEnumerator { // 摘要:獲取集合中位於枚舉數當前位置的元素 // 返回結果:集合中位於枚舉數當前位置的元素。 object Current { get; }
// 摘要:將枚舉數推進到集合的下一個元素。 // 返回結果:如果枚舉數已成功地推進到下一個元素,則為 true;如果枚舉數傳遞到集合的末尾,則為 false。 // 異常:T:System.InvalidOperationException:創建枚舉器後,已修改該集合。 bool MoveNext(); // 摘要:將枚舉數設置為其初始位置,該位置位於集合中第一個元素之前。 // 異常: T:System.InvalidOperationException: 創建枚舉器後,已修改該集合。 void Reset(); }
有了枚舉器,就可以使用MoveNext和Current來模仿foreach迴圈:
int[] arrInt = new int[] { 11, 12, 13, 14 }; foreach (var item in arrInt) { Console.WriteLine(item); } var enumerator = arrInt.GetEnumerator(); while (enumerator.MoveNext()) { var item = (int)enumerator.Current; Console.WriteLine(item); }
下麵是一個使用IEnumerator和IEnumerable的小例子:
class Program { static void Main(string[] args) { var myColor = new MyColors(); foreach (var item in myColor) { Console.WriteLine(item); } } } class ColorEnumeraotr : IEnumerator { string[] _colors; int _position = -1; public ColorEnumeraotr(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 == -1) throw new InvalidOperationException(); if (_position >= _colors.Length) throw new InvalidOperationException(); return _colors[_position]; } } public bool MoveNext() { if (_position < _colors.Length - 1) { _position++; return true; } return false; } public void Reset() { _position = -1; } } class MyColors : IEnumerable { string[] Colors = new string[] { "red","blue","yellow","green","white"}; public IEnumerator GetEnumerator() { return new ColorEnumeraotr(Colors); } }View Code
三 泛型枚舉介面
非泛型介面的實現不是類型安全的,它返回object的引用,然後必須轉化為實際類型。
所以實際上,在多數情況下,我們應該使用泛型版本的IEnumerable<T>和IEnumerator<T>。
泛型介面的枚舉器是類型安全的,它返回實際類型的引用。
IEnumerable<T>介面的GetEnumerator方法返回實現IEnumerator<T>的枚舉器對象。
實現IEnumerator<T>的枚舉器類的Current屬性,它返回T類型的對象。
四 迭代器
C#從2.0版本開始提供了更簡單的創建枚舉器和可枚舉類型的方式。這種結構叫做迭代器。
迭代器塊是有一個或多個yield語句的代碼塊。
迭代器塊與其他代碼塊不同。其他塊包含的語句是命令式的,也就是塊中的語句依次執行,最後離開塊。
而迭代器塊是描述了希望編譯器為我們創建的枚舉器類的行為,迭代器塊中的代碼描述瞭如何枚舉元素。
迭代器塊有兩個特殊語句:
- yield return : 指定了序列中返回的下一項;
- yield break : 指定在序列中沒有其他項。
//產生枚舉器的迭代器 public IEnumerator<string> IteratorMethod1() { yield return "red"; yield return "blue"; yield return "yellow"; } //產生可枚舉類型的迭代器 public IEnumerable<string> IteratorMethod2() { yield return "red"; yield return "blue"; yield return "yellow"; }
4.1 使用迭代器來創建枚舉器
下麵代碼演示如何使用迭代器來創建枚舉器:
class Program { static void Main(string[] args) { var mc = new MyClass(); foreach (var item in mc) { Console.WriteLine(item); } } } class MyClass { public IEnumerator<string> GetEnumerator() { return MyEnumerator(); //返回枚舉器 } //迭代器 public IEnumerator<string> MyEnumerator() { yield return "red"; yield return "blue"; yield return "yellow"; } }View Code
4.2 使用迭代器來創建可枚舉類型
下麵代碼演示如何使用迭代器來創建可枚舉類型:
class Program { static void Main(string[] args) { var mc = new MyClass(); foreach (var item in mc) { Console.WriteLine(item); } foreach (var item in mc.MyEnumerable()) { Console.WriteLine(item); } } } class MyClass { public IEnumerator<string> GetEnumerator() { IEnumerable<string> myEnumerable = MyEnumerable(); return myEnumerable.GetEnumerator(); } public IEnumerable<string> MyEnumerable() { yield return "red"; yield return "blue"; yield return "yellow"; } }View Code