迭代程序實現
編譯器生成 的嵌套類維持迭代狀態。當在 foreach 循環中(或在直接迭代代碼中)首次調用 迭代程序時,編譯器為 GetEnumerator 生成的代碼將創建一個帶有 reset 狀態 的新的迭代程序對象(嵌套類的一個實例)。在 foreach 每次循環調用迭代程序 的 MoveNext 方法時,它都從前一次 yield return 語句停止的地方開始執行。 只要 foreach 循環執行,迭代程序就會維持它的狀態。然而,迭代程序對象(以 及它的狀態)在多個 foreach 循環之間並不保持一致。因此,再次調用 foreach 是安全的,因為您將使新的迭代程序對象開始新的迭代。這就是為什麼 IEnumerable <ItemType> 沒有定義 Reset 方法的原因。
但是嵌套 迭代程序類是如何實現的呢?並且如何管理它的狀態呢?編譯器將一個標准方法 轉換成一個可以被多次調用的方法,此方法使用一個簡單的狀態機在前一個 yield return 語句之後恢復執行。您需要做的只是使用 yield return 語句指示 編譯器產生什麼以及何時產生。編譯器具有足夠的智能,它甚至能夠將多個 yIEld return 語句按照它們出現的順序連接起來:
public class CityCollection : IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
yIEld return "New York";
yIEld return "Paris";
yIEld return "London";
}
}
讓我們看一看在下面幾行代碼中顯示的該類的 GetEnumerator 方法:
public class MyCollection : IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
//Some iteration code that uses yIEld return
}
}
當編譯 器遇到這種帶有 yield return 語句的類成員時,它會插入一個名為 GetEnumerator$<random unique number>__IEnumeratorImpl 的嵌套類的 定義,如圖 5 中 C# 偽代碼所示。(記住,本文所討論的所有特征 — 編 譯器生成的類和字段的名稱 — 是會改變的,在某些情況下甚至會發生徹底 的變化。您不應該試圖使用反射來獲得這些實現細節並期望得到一致的結果。) 嵌套類實現了從類成員返回的相同 IEnumerable 接口。編譯器使用一個實例化的 嵌套類型來代替類成員中的代碼,將一個指向集合的引用賦給嵌套類的 <this> 成員變量,類似於圖 2 中所示的手動實現。實際上,該嵌套類是 一個提供了 IEnumerator 的實現的類。
Figure 5The Compiler- generated Iterator
public class MyCollection : IEnumerable<string>
{
public virtual IEnumerator<string> GetEnumerator()
{
GetEnumerator$0003__IEnumeratorImpl impl;
impl = new GetEnumerator$0003__IEnumeratorImpl;
impl.<this> = this;
return impl;
}
private class GetEnumerator$0003__IEnumeratorImpl :
IEnumerator<string>
{
public MyCollection <this>; // Back reference to the collection
string $_current;
// state Machine members go here
string IEnumerator<string>.Current
{
get
{
return $_current;
}
}
bool IEnumerator<string>.MoveNext()
{
//State Machine management
}
IDisposable.Dispose()
{
//State Machine cleanup if required
}
}
}
圖 5