如何在 C# 中使用 yield
yield關(guān)鍵詞是在 C# 2.0 中被引入的,我們都知道實(shí)現(xiàn)了 IEnumerable 接口的類都可以用于被 foreach 迭代,這是因?yàn)?IEnumerable 接口中提供了一個(gè)可迭代的 GetEnumerator() 方法,代碼定義如下:
????public?interface?IEnumerable
????{
????????IEnumerator?GetEnumerator();
????}
現(xiàn)在你也可以使用 yield 關(guān)鍵詞來指定某些方法也是可以被迭代的,通常 C# 中有兩種 yield 的語法格式:yield return 和 yield break。
為什么要使用 yield 關(guān)鍵詞
yield關(guān)鍵詞可以實(shí)現(xiàn)一種 狀態(tài)迭代 而不需要提前創(chuàng)建好一個(gè)臨時(shí)集合,換句話說,當(dāng)你在迭代器中使用 yield return 時(shí),在數(shù)據(jù)返回之前你不需要創(chuàng)建一個(gè)臨時(shí)集合來存儲數(shù)據(jù),你可以利用 yield return 一次返回集合中的一個(gè)元素,同時(shí)你也可以在方法和get訪問器中使用帶有迭代的 yield return 語句,值得注意的是,當(dāng)每次執(zhí)行 yield return 語句后,控制權(quán)都會轉(zhuǎn)交給調(diào)用者。
說了這么多,如果有點(diǎn)懵的話,我們來看一個(gè)例子,下面的代碼展示了如何使用 yield 關(guān)鍵詞來返回 Fibonacci 數(shù)字,這個(gè)方法接收一個(gè)int類型的參數(shù)。
????????static?IEnumerable<int>?GenerateFibonacciNumbers(int?n)
????????{
????????????for?(int?i?=?0,?j?=?0,?k?=?1;?i?????????????{
????????????????yield?return?j;
????????????????int?temp?=?j?+?k;
????????????????j?=?k;
????????????????k?=?temp;
????????????}
????????}
上面的代碼中 yield return j 在不退出 for 循環(huán)的情況下逐個(gè)返回斐波那契數(shù),換句話說,這個(gè)迭代狀態(tài)是被保留的,下面的代碼展示了如何調(diào)用 GenerateFibonacciNumbers()。
????????????foreach?(int?x?in?GenerateFibonacciNumbers(10))
????????????{
????????????????Console.WriteLine(x);
????????????}
下面是僅供參考的完整代碼。
????class?Program
????{
????????static?void?Main(string[]?args)
????????{
????????????foreach?(int?x?in?GenerateFibonacciNumbers(10))
????????????{
????????????????Console.WriteLine(x);
????????????}
????????}
????????static?IEnumerable<int>?GenerateFibonacciNumbers(int?n)
????????{
????????????for?(int?i?=?0,?j?=?0,?k?=?1;?i?????????????{
????????????????yield?return?j;
????????????????int?temp?=?j?+?k;
????????????????j?=?k;
????????????????k?=?temp;
????????????}
????????}
????}

也許你注意到了,上面的代碼并沒有創(chuàng)建一個(gè) list 或者 array 去存放那些輸出到控制臺的斐波那契額數(shù)。
yield 關(guān)鍵詞的另一個(gè)優(yōu)點(diǎn)在于可以按需創(chuàng)建和返回你需要的數(shù),下面的代碼展示了 Get方法器 中僅返回 1-10 之間的偶數(shù)。
????????public?static?IEnumerable<int>?EvenNumbers
????????{
????????????get
????????????{
????????????????for?(int?i?=?1;?i?<=?10;?i++)
????????????????{
????????????????????if?((i?%?2)?==?0)
????????????????????????yield?return?i;
????????????????}
????????????}
????????}
你也可以使用 yield break 來提前中斷一個(gè)迭代鏈,如下代碼所示:
????????public?IEnumerable?GetData(IEnumerable?items)
????????{
????????????if?(null?==?items)
????????????????yield?break;
????????????foreach?(T?item?in?items)
????????????????yield?return?item;
????????}
幾點(diǎn)原則
當(dāng)你在用 yield 時(shí),請記住如下幾點(diǎn)。
yield return不能套在try-catch中,否則會報(bào)錯。

yield break不能放在 finally 中。yield 方法的返回類型只能是
IEnumerable, IEnumerable。, IEnumerator,IEnumerator 在 yiled 的方法參數(shù)中不能使用 ref,out 標(biāo)記。
不能將
yield return和yield break放在匿名方法中。不能將
yield return和yield break放在unsafe方法中。
譯文鏈接:https://www.infoworld.com/article/3122592/my-two-cents-on-the-yield-keyword-in-c.html
