迭代器是一個值序列(集合)上的一個只讀且只向前移動的游標。迭代器要么實現了IEnumerator接口,要么實現了IEnumerator<T>接口。
從技術的角度看,如果一個對象有MoveNext方法以及Current屬性,那么我們就可以將其看作一個迭代器。
我們可以使用foreach語句去迭代一個可列舉對象。可迭代的對象其實就是一個序列的邏輯體現。可列舉的對象不但自身就是一個游標,而且它還可以生成一個游標迭代自己。因此,可列舉的對象有兩個特性
列舉模式:

class Enumerator{ public IteratorVariableType Current {get {...}} public bool MoveNext() {...}}class Enumerable{ public Enumerator GetEnumerator() {...}}Enumeration pattern 為了更好的理解上面的概率和模式,我們來看下面的兩個例子

foreach (char c in "CSharp") Console.WriteLine(c);Sample 1

using (var enumerator = "CSharp".GetEnumerator()){ while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); }}Sample 2Sample1采取了foreach這樣的高級方式去迭代字符串(因為字符串類實現了CharEnumerator);而Sample2則使用了底層的方式完成對字符串的迭代。 對于Sample我們使用了using語句,這是因為CharEnumerator實現了IDisposable接口,下面的代碼顯示了CharEnumrator的大部分代碼(來自微軟官方)

public sealed class CharEnumerator : IEnumerator, IDisposable{ PRivate String str; private int index; private char currentElement; internal CharEnumerator(String str) { this.str = str; this.index = -1; } public bool MoveNext() { if (index < (str.Length - 1)) { index++; currentElement = str[index]; return true; } else index = str.Length; return false; } public void Dispose() { if (str != null) index = str.Length; str = null; } public char Current { get { return currentElement; } } public void Reset() { currentElement = (char)0; index = -1; }}CharEnumerator我們可使用一行語句實例一個可列舉的對象。比如:IList<Int> list = new List<int>{1,2,3};編譯時,編譯器會自動翻譯為:

IList<Int> list = new List<int>();list.Add(1);list.Add(2);list.Add(3);Translated Code
這是因為該列舉對象實現了IEnumerable接口,而且還包含了Add方法。為了驗證此點,我們可以通過查看IL代碼的方式來確認:

IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor() IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: ldc.i4.1 IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) IL_000e: nop IL_000f: ldloc.1 IL_0010: ldc.i4.2 IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) IL_0016: nop IL_0017: ldloc.1 IL_0018: ldc.i4.3 IL_0019: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) IL_001e: nop IL_001f: ldloc.1 IL_0020: stloc.0 IL_0021: call string [mscorlib]System.Console::ReadLine()IL Code
既然foreach可應用于列舉,那么一個列舉可以生成一個迭代器。很繞口很困惑是吧,我們先來看下面的例子:使用迭代器返回斐波納契數列

static IEnumerable<int> Fibonacci(int number){ for(int i=0, prevFib=1, curFib=1;i<number;i++) { yield return prevFib; int newFib = prevFib + curFib; prevFib = curFib; curFib = newFib; }}// teststatic void Main(string[] args){ foreach (int f in Fibonacci(10)) Console.WriteLine(f); Console.ReadLine();}Fibonacci請注意,在上面的代碼中,我們使用了yield return。那么它和return有什么區別呢?return:從方法中返回一個值yield return:從當前的迭代器中生成下一個元素。yield語句每執行一次,程序的控制權就退還給調用者,而被調用者的狀態仍然保留,這就使得方法在調用者列舉下一個元素的時候能繼續執行。被調用者的狀態的生命周期取決于列舉,正因為如此,當調用者完成列舉后,被調用者的狀態得以釋放。
迭代器可以是包含了一個或多個yield語句的方法、屬性、或所引器。迭代器必須返回下面四個類型之一:IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T>
再繼續下一步之前,我們看一下IEnumerable接口和IEnumerator的定義

public interface IEnumerator{ bool MoveNext(); Object Current {get; } void Reset();}public interface IEnumerable{ IEnumerator GetEnumerator();}IEnumerator & IEnumerable迭代器與列舉有不一樣的語法,在于迭代器需要返回可列舉的接口或者列舉器接口。
迭代器可以進一步用于創建迭代。為了證實這點,我們可以擴展我們斐波納契數列例子

static IEnumerable<int> Fibonacci(int number){ for(int i=0, prevFib=1, curFib=1;i<number;i++) { yield return prevFib; int newFib = prevFib + curFib; prevFib = curFib; curFib = newFib; }}static IEnumerable<int> EvenNumbers(IEnumerable<int> sequence){ foreach (int x in sequence) if (x % 2 == 0) yield return x;}static void Main(string[] args){ foreach (int f in EvenNumbers(Fibo
新聞熱點
疑難解答