迭代器和yield
2012-08-05?tc莊稼漢
迭代器是一種方法、get 訪問器或運算符,它通過使用 yield 關(guān)鍵字對數(shù)組或集合類執(zhí)行自定義迭代。 yield 返回語句會導(dǎo)致源序列中的元素在訪問源序列中的下一個元素之前立即返回給調(diào)用方。 盡管您以方法的形式編寫迭代器,但編譯器會將其轉(zhuǎn)換為一個實際上是狀態(tài)機的嵌套類。 只要客戶端代碼中的 foreach 循環(huán)繼續(xù)進行,此類就會跟蹤迭代器的位置。
將使用 foreach 語句從客戶端代碼中調(diào)用迭代器。 例如,您可以為類創(chuàng)建一個迭代器,該迭代器將按相反順序返回元素,或在迭代器返回元素之前對每個元素執(zhí)行操作。 在為類或結(jié)構(gòu)創(chuàng)建迭代器時,您不必實現(xiàn)整個 IEnumerator 接口。 當編譯器檢測到迭代器時,它將自動生成 IEnumerator 或 IEnumerator<T> 接口的 Current、MoveNext 和 Dispose 方法。
一、迭代器概述
-
迭代器是可以返回相同類型的值的有序序列的一段代碼。
-
迭代器可用作方法、運算符或 get 訪問器的代碼體。
-
迭代器代碼使用 yield return 語句依次返回每個元素。 yield break 將終止迭代。
-
可以在類中實現(xiàn)多個迭代器。 每個迭代器都必須像任何類成員一樣有唯一的名稱,并且可以在 foreach 語句中被客戶端代碼調(diào)用,如下所示:foreach(int x in SampleClass.Iterator2){}。
-
迭代器的返回類型必須為 IEnumerable、IEnumerator、IEnumerable<T> 或 IEnumerator<T>。
- 迭代器是 LINQ 查詢中延遲執(zhí)行行為的基礎(chǔ)。
yield 關(guān)鍵字用于指定返回的一個或多個值。 到達 yield return 語句時,會保存當前位置。 下次調(diào)用迭代器時將從此位置重新開始執(zhí)行。
迭代器對集合類特別有用,它提供一種簡單的方法來迭代復(fù)雜的數(shù)據(jù)結(jié)構(gòu)(如二進制樹)。
二、yield關(guān)鍵字
yield 關(guān)鍵字向編譯器指示它所在的方法是迭代器塊。 編譯器生成一個類來實現(xiàn)迭代器塊中表示的行為。 在迭代器塊中,yield 關(guān)鍵字與 return 關(guān)鍵字結(jié)合使用,向枚舉器對象提供值。 這是一個返回值,例如,在 foreach 語句的每一次循環(huán)中返回的值。 yield 關(guān)鍵字也可與 break 結(jié)合使用,表示迭代結(jié)束。 有關(guān)迭代器的更多信息,請參見迭代器(C# 編程指南)。
使用迭代塊,編譯器會生成一個yield類型,其中包含一個狀態(tài)機,yield類型執(zhí)行IEnumerator和IDisposable接口的屬性和方法.GetEnumerator()方法實例化并返回一個新的yield類型.在yield類型中,變量state定義了迭代的當前位置,每次調(diào)用MoveNext()時,當前位置都會改變.MoveNext()封裝了迭代塊的代碼,設(shè)置了current變量的值,使Current屬性根據(jù)位置返回一個對象。
下面的示例演示兩種形式的 yield 語句。
?
yield注意事項:
在 yield return 語句中,將計算 expression 并將結(jié)果以值的形式返回給枚舉器對象;expression 必須可以隱式轉(zhuǎn)換為 yield 類型的迭代器。
在 yield break 語句中,控制權(quán)將無條件地返回給迭代器的調(diào)用方,該調(diào)用方為枚舉器對象的 IEnumerator.MoveNext 方法(或其對應(yīng)的泛型 System.Collections.Generic.IEnumerable<T>)或 Dispose 方法。
yield 語句只能出現(xiàn)在 iterator 塊中,這種塊可作為方法、運算符或訪問器的主體實現(xiàn)。 這類方法、運算符或訪問器的體受以下約束的控制:
-
不允許不安全塊。
-
方法、運算符或訪問器的參數(shù)不能是 ref 或 out。
-
yield return 語句不能放在 try-catch 塊中的任何位置。 該語句可放在后跟 finally 塊的 try 塊中。
-
yield break 語句可放在 try 塊或 catch 塊中,但不能放在 finally 塊中。
yield 語句不能出現(xiàn)在匿名方法中。 有關(guān)更多信息,請參見 匿名方法(C# 編程指南)。
當和 expression 一起使用時,yield return 語句不能出現(xiàn)在 catch 塊中或含有一個或多個 catch 子句的 try 塊中。 有關(guān)更多信息,請參見 異常處理語句(C# 參考)。
三、使用迭代器
1.創(chuàng)建迭代器最常用的方法是對 IEnumerable 接口實現(xiàn) GetEnumerator 方法,例如:
?
GetEnumerator 方法的存在使得類型成為可枚舉的類型,并允許使用 foreach 語句。 如果上面的方法是 ListClass 的類定義的一部分,則可以對該類使用 foreach,如下所示:
?
foreach 語句調(diào)用 ListClass.GetEnumerator() 并使用返回的枚舉數(shù)來循環(huán)訪問值。
2.還可以使用命名的迭代器以支持通過不同的方式循環(huán)訪問同一數(shù)據(jù)集合。 例如,您可以提供一個按升序返回元素的迭代器,而提供按降序返回元素的另一個迭代器。 迭代器還可以帶有參數(shù),以便允許客戶端控制全部或部分迭代行為。 下面的迭代器使用命名的迭代器 SampleIterator 實現(xiàn) IEnumerable 接口:
?
命名的迭代器的調(diào)用方法如下(ps:第一種實現(xiàn)GetEnumerator方法,實際上就是定義一個支持IEnumerable或者IEnumrator的可枚舉類型,所以foreach (int i in listClass1),這里的listClass1是listClass類型的實例對象,而下面這個foreach (int n in test.SampleIterator(1, 10))就是使用的迭代器的名稱。):
?
可以在同一個迭代器中使用多個 yield 語句,如下面的示例所示:
?
然后可以使用下面的 foreach 語句輸出結(jié)果:
?
此示例顯示以下文本:
With an iterator, more than one value can be returned.
在 foreach 循環(huán)的每次后續(xù)迭代(或?qū)?IEnumerator.MoveNext 的直接調(diào)用)中,下一個迭代器代碼體將從前一個 yield 語句之后開始,并繼續(xù)下一個語句直至到達迭代器體的結(jié)尾或遇到 yield?break 語句。
迭代器不支持 IEnumerator.Reset 方法。 若要從頭開始重新循環(huán)訪問,必須獲取新迭代器。
轉(zhuǎn)載于:https://www.cnblogs.com/shandong/archive/2012/08/05/2623989.html
總結(jié)
- 上一篇: [状态压缩DP] COJ 1129 送货
- 下一篇: 免税店可以刷信用卡吗?购物后一定要注意这