C# - yield return 使用方法

在之前幾篇有介紹過一些C#的修飾詞,這次要介紹的是yield修飾詞,你一定覺得C#怎麼這麼多修飾詞。是的,C#的修飾詞就是這麼多,但這次要介紹的yield蠻有趣的,在C++或java可是找不到這種功能的修飾詞。


yield return的用法舉個例子來說,當我在迴圈中遍歷(iterate)每個元素,如果遇到符合條件的元素時,一般的做法通常是new一個集合的instance,再逐一將符合的元素加入集合中,最後再回傳整個整理後的集合;yield return的做法為當遇到符合條件的元素時,即刻將該元素回傳回上一層進行後續運算,後續運算完後再回到迴圈中找尋下一個元素。在MSDN上對於yield的使用上還有提到一些限制:

  1. 回傳類型必須為IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T>
  2. 不可包含任何 ref 與 out 參數
  3. 匿名方法與unsafe區塊不可使用yield

下面是用圖解(下面程式碼流程)來表示yield return與一般做法的差異:
yield:
一開始list只單純指向GetEvenNumByYield方法而不實際執行,當foreach開始使用到list時,才執行GetEvenNumByYield方法,執行過程中只要遇到符合的元素就直接回傳回去進行sum的加總運算,sum運算後再回到GetEvenNumByYield方法進行下一個iteration。

一般做法(Temp):

一開始list會呼叫GetEvenNumByTemp方法運算後取得集合,GetEvenNumByTemp方法內部運算時會將符合的元素暫存在一個temp的list中,整個運算完後將temp list回傳給主程式,接著主程式在利用這個處理完的list一一取出元素進行sum加總運算。

以下為範例程式碼,顯示 yield return 與一般Temp List的差異

static void Main(string[] args)
{
    var myList = new List<int>();
    for(var i = 0; i < 10000000; i++)
    {
        myList.Add(i);
    }
 
    var timeList = new List<long>();
    for(var i = 0; i < 10; i++)
    {
        var sum = 0;
 var sw = new Stopwatch();
 sw.Start();
 var list = getEvenNumByYield(myList);
 //var list = getEvenNumByLinq(myList);
 //var list = getEvenNumByTemp(myList);
 foreach(var l in list)
 {
     sum += l;
 }
  
 // Reuse collection
 foreach(var l in list)
 {
     sum -= l;
 }
 sw.Stop();
 timeList.Add(sw.ElapsedMilliseconds);
 Console.WriteLine("Exe time: {0} ms", sw.ElapsedMilliseconds);
    }
    Console.WriteLine("Average: {0} ms", (double) timeList.Sum() / 10);
    Console.WriteLine("Memory Usage: {0}", Process.GetCurrentProcess().PrivateMemorySize64);
}

static IEnumerable<int> getEvenNumByYield(IEnumerable<int> collection)
{
    foreach(var i in collection)
    {
        if(i % 2 == 0) yield return i;
    }
}

static IEnumerable<int> getEvenNumByLinq(IEnumerable<int> collection)
{
    return collection.ToList().Where(i => i % 2 == 0);
}

static IEnumerable<int> getEvenNumByTemp(IEnumerable<int> collection)
{
    var temp = new List<int>();
    foreach(var i in collection)
    {
        if(i % 2 == 0) temp.Add(i);
    }
    return temp;
}



參考來源:
MSDN - yield (C# 參考)




留言

張貼留言