所有 IEnumerator<T> 對象實質上都是狀態機,可跟蹤集合中要枚舉的項目。foreach 循環調 用 MoveNext 時,MoveNext 方法會將狀態機推進到下一個項目。在我的 ArrayEnumerator 類中,如果所 有項目均已被枚舉(m_index 已經是 0,即最後一項),則 MoveNext 返回 false,否則,它會遞減 m_index 字段並返回 true。Current 屬性只會返回狀態機當前位置所反映的項目。
正如您所看到的,實現代碼枯燥乏味,因此我將一些接口方法留空以簡化編碼。我還省略了一些錯誤 檢查代碼。同時,數組是一個相當簡單的數據結構,因此為它編寫枚舉器簡直輕而易舉。其他數據結構( 如鏈接列表或樹)則會大大增加代碼的復雜性。
還應指出的一點是,您不能通過 foreach 語句直接使用我的 ArrayEnumerator<T> 類。換句話 說,這將無法編譯:
foreach (Int32 num in new ArrayEnumerator<Int32>(numbers))
Console.WriteLine(num);
foreach 語句等待 in 關鍵字後面出現一個集合(其類型實現 IEnumerable<T> 的對象),而 不是 IEnumerator<T>。因此,為了使用我的 ArrayEnumerator<T>,我還必須定義實現 IEnumerable<T> 的類,我的 ArrayEnumerator<T> 類也顯示在圖 3 中。現在,已經定義了 ArrayEnumerable<T> 類之後,我便能夠編寫可順利編譯和執行的以下代碼:
foreach (Int32 num in new ArrayEnumerable<Int32>(numbers))
Console.WriteLine(num);
哇!為了遍歷項目集合,需要編寫相當多的代碼。要是有一些技術使得代碼的編寫工作變簡單該有多 好。幸運的是,確實有 — 那就是 C# 2.0 迭代器功能。迭代器允許您編寫可返回有序值序列的單個成員 。通過簡單語法來表達應如何返回值,以及 C# 編譯器如何將代碼塊轉換為實現 IEnumerable<T> 、IEnumerable、IEnumerator<T>、IEnumerator 和 IDisposable 接口的完備類,便可實現此編寫 目的。
使用 C# 迭代器功能,我將 AsyncEnumerable<T> 類和 AsyncEnumerator<T> 類替換為 以下一個成員:
private static IEnumerable<T> ArrayIterator<T>(T[] array) {
for (Int32 index = array.Length-1; index >= 0; index--)
yIEld return array[index];
}
而且還可以使用 foreach 語句調用此成員,如下所示:
foreach (Int32 item in ArrayIterator(numbers))
Console.WriteLine(item);
還有一個額外的好處,由於這是泛型方法(相對於泛型類),C# 類型推斷也會介入,因此我不需要編 寫 ArrayIterator<Int32>,從而也令 foreach 代碼更加簡單。
請注意,由於我的迭代器成員會返回 IEnumerable<T>,因此編譯器會生成可實現 IEnumerable<T>、IEnumerable、IEnumerator<T>、IEnumerator 和 IDisposable 的代碼。 但是,您可以編寫返回 IEnumerator<T> 的迭代器成員,在這種情況下,編譯器生成只實現 IEnumerator<T>、IEnumerator 和 IDisposable 接口成員的類定義。
還要指出的一點是,您可以定義返回類型為非泛型 IEnumerable 或 IEnumerator 接口的迭代器成員 ,然後編譯器會定義 IEnumerable<Object>/IEnumerable(如果將 IEnumerable 指定為返回類型 )、IEnumerator<Object>/IEnumerator 和 IDisposable。
在迭代器中,yield return 語句會有效指示編譯器從方法返回何值(集合項目)。然而,此時迭代器 實際上並不會結束執行過程;而是掛起該執行。下次調用 MoveNext 時,該迭代器會在緊跟著 yield return 語句後的語句處重新開始其執行。除 yield return 之外,迭代器還包括 yIEld break 語句,在 執行時可使 MoveNext 方法返回 false,強制終止 foreach 循環。從迭代器成員退出也會導致 MoveNext 方法返回 false,強制終止 foreach 循環。
異步編程
對異步編程而言,迭代器帶來了諸多切實的好處。首先,在迭代器內,您可以編寫訪問參數和局部變 量的代碼。但是,編譯器實際上會將這些變量封裝到類字段(就像為匿名方法和 lambda 表達式所做的那 樣)。
其次,編譯器使您能夠編寫序列代碼,但也可以中途掛起方法,並在稍後繼續該執行(可能使用不同 的線程)。
再次,您可以在迭代器內使用各種編程構造,如 try/catch/finally 語句、lock 語句、using 語句 ,甚至是循環(for、while 和 foreach 語句)。編譯器會自動重新編寫代碼,以便這些構造能維護其語 義。
對記錄而言,有一些與迭代器相關的限制:
迭代器成員的簽名不能包含任何 out 或 ref 參數。
迭代器不能包含 return 語句(yIEld return 是可以的)。
迭代器不能包含任何不安全代碼。