C# 中 yield return 和 yield break 關鍵字的用法

yield 關鍵字向編譯器指示它所在的方法是迭代器塊。 編譯器生成一個類來實現迭代器塊中表示的行爲。 在迭代器塊中,yield 關鍵字與 return 關鍵字結合使用,向枚舉器對象提供值。 這是一個返回值,例如,在 foreach 語句的每一次循環中返回的值。 yield 關鍵字也可與 break 結合使用,表示迭代結束。 有關迭代器的更多信息,請參見迭代器(C# 編程指南) 下面的示例演示兩種形式的 yield 語句。

yield return <expression>;
yield break;

在 yield return 語句中,將計算 expression 並將結果以值的形式返回給枚舉器對象;expression 必須可以隱式轉換爲 yield 類型的迭代器。

yield break 語句中,控制權將無條件地返回給迭代器的調用方,該調用方爲枚舉器對象的 IEnumerator.MoveNext 方法(或其對應的泛型 System.Collections.Generic.IEnumerable<T>)或 Dispose 方法。

yield 語句只能出現在 iterator 塊中,這種塊可作爲方法、運算符或訪問器的主體實現。 這類方法、運算符或訪問器的體受以下約束的控制:

  • 不允許不安全塊。

  • 方法、運算符或訪問器的參數不能是 ref out

  • yield return 語句不能放在 try-catch 塊中的任何位置。 該語句可放在後跟 finally 塊的 try 塊中。

  • yield break 語句可放在 try 塊或 catch 塊中,但不能放在 finally 塊中。

yield 語句不能出現在匿名方法中。 有關更多信息,請參見 匿名方法(C# 編程指南)

當和 expression 一起使用時,yield return 語句不能出現在 catch 塊中或含有一個或多個 catch 子句的 try 塊中。 有關更多信息,請參見 異常處理語句(C# 參考)

在下面的示例中,迭代器塊(這裏是方法 Power(int number, int power))中使用了 yield 語句。 當調用 Power 方法時,它返回一個包含數字冪的可枚舉對象。 注意 Power 方法的返回類型是 System.Collections.IEnumerable(一種迭代器接口類型)。

public class List
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/

迭代器是一種方法、get 訪問器或運算符,它通過使用 yield 關鍵字對數組或集合類執行自定義迭代。 yield 返回語句會導致源序列中的元素在訪問源序列中的下一個元素之前立即返回給調用方。 儘管您以方法的形式編寫迭代器,但編譯器會將其轉換爲一個實際上是狀態機的嵌套類。 只要客戶端代碼中的 foreach 循環繼續進行,此類就會跟蹤迭代器的位置。

注意注意

若要了解編譯器在後臺執行了什麼操作,請使用 ILDASM.exe 工具來查看爲迭代器方法生成的中間語言 (IL) 代碼。

將使用 foreach 語句從客戶端代碼中調用迭代器。 例如,您可以爲類創建一個迭代器,該迭代器將按相反順序返回元素,或在迭代器返回元素之前對每個元素執行操作。 在爲結構創建迭代器時,您不必實現整個 IEnumerator 接口。 當編譯器檢測到迭代器時,它將自動生成 IEnumeratorIEnumerator<T> 接口的 CurrentMoveNextDispose 方法。

  • 迭代器是可以返回相同類型的值的有序序列的一段代碼。

  • 迭代器可用作方法、運算符或 get 訪問器的代碼體。

  • 迭代器代碼使用 yield return 語句依次返回每個元素。 yield break 將終止迭代。

  • 可以在類中實現多個迭代器。 每個迭代器都必須像任何類成員一樣有唯一的名稱,並且可以在 foreach 語句中被客戶端代碼調用,如下所示:foreach(int x in SampleClass.Iterator2){}

  • 迭代器的返回類型必須爲 IEnumerableIEnumeratorIEnumerable<T>IEnumerator<T>

  • 迭代器是 LINQ 查詢中延遲執行行爲的基礎。

yield 關鍵字用於指定返回的一個或多個值。 到達 yield return 語句時,會保存當前位置。 下次調用迭代器時將從此位置重新開始執行。

迭代器對集合類特別有用,它提供一種簡單的方法來迭代複雜的數據結構(如二進制樹)。

在本示例中,DaysOfTheWeek 類是將一週中的各天作爲字符串進行存儲的簡單集合類。 foreach 循環每迭代一次,都返回集合中的下一個字符串。

public class DaysOfTheWeek : System.Collections.IEnumerable
{
    string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

    public System.Collections.IEnumerator GetEnumerator()
    {
        for (int i = 0; i < days.Length; i++)
        {
            yield return days[i];
        }
    }
}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}
// Output: Sun Mon Tue Wed Thr Fri Sat

 
    ★IEnumerator對象:
        public IEnumerator<int> GetEnumerator()//注意方法名
        {
            yield return 0;
            yield return 1;
            yield return 2;
            yield return 3;
        }
可以作用於foreach語句,但是方法所在的類用在foreach語句中,並且必須使用GetEnumerator作爲獲取迭代器的方法名。
        Integers ints = new Integers();//Integers是GetEnumerator()方法所在的類。
        foreach (int i in ints)
        {
            Console.WriteLine(i.ToString());
        }
 
PS:幾乎所有的對yield return和直接return list的測試結果都會告訴你,yield return更慢。但是當你真正瞭解yield之後,相信多數情況下都會選擇yield return的。
 
今天項目中用到了 yield return ,主要是爲了實現 延遲迭代的需求,所以就寫了這個小的 Demo,希望對你有幫助! 代碼如下:
using System;
using System.Collections.Generic;
  
namespace ConAppYield
{
    class Program
    {
        static void Main(string[] args)
        {
            // Yield 的好處是 延遲遍歷,只有在遍歷具體的數據時纔去執行對應的代碼,Linq 中大量的擴展方法用到了,比如:select 等等
            Console.WriteLine("==偶數年齡如下:==");
            IEnumerable<int> query = Students.GetEvenNumbers();
            foreach (var item in query)
            {
                Console.WriteLine(item);
            }
              
              
            Console.WriteLine("==下面用 do while 模擬 C# 中的 foreach 循環,說到底,foreach 循環只是 C# 的語法糖而已! ==");
            Console.WriteLine("==同時,foreach 循環也是一種設計模式,叫 迭代模式==");
            IEnumerator<int> newQuery = query.GetEnumerator();
            int i = 0;
            while(newQuery.MoveNext())
            {
                i++;
                Console.WriteLine("集合中的第 "+ i +" 個數:" + newQuery.Current);
            }
            Console.ReadKey();
        }
    }
  
    class Students
    {
        private static readonly int[] ages = new int[] { 1,2,3,4,5,6,7,8,9,10 };
  
        public static IEnumerable<int> GetEvenNumbers()
        {
            int agesLength = ages.Length;
            for (int i = 0; i < agesLength; i++)
            {
                if (ages[i] % 2 == 0)
                {
                    yield return ages[i];
                }
                else
                {
                    //yield break; //yield break 可以終止循環,即:只要碰到的數不是偶數,則立即終止循環
                }
            }
            /* 下面的寫法錯誤:無法在包含 catch 子句的 Try 塊體中生成值 */
            /*
            try
            {
                foreach (var item in ages)
                {
                    if (item % 2 == 0)
                    {
                        yield return item;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }*/
  
        }
    }
}
謝謝瀏覽!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章