Home [C# 筆記] 疊代器 (Iterators) - yield return 語法糖
Post
Cancel

[C# 筆記] 疊代器 (Iterators) - yield return 語法糖

疊代器 (Iterators) 的出現是為了「讓foreach 語法更加靈活與簡單」。

  • yield關鍵字用於遍歷循環中
  • yield return用於返回IEnumerable<T>
  • yield break用於終止循環遍歷

當我們編寫 C# 程式碼時,經常需要處理大量的資料集合。在傳統的方式中,我們往往需要先將整個資料集合載入記憶體中,然後再進行操作。但是如果資料集合非常大,這種方式就會導致記憶體佔用過高,甚至可能導致程式崩潰。

C# 中的yield return機制可以幫助我們解決這個問題。透過使用yield return,我們可以將資料集合按需生成,而不是一次性生成整個資料集合。這樣可以大大減少記憶體佔用,並且提高程式的效能。

Iterators 區塊

1
2
3
4
5
6
7
8
9
  public class MyList 
  {
      public IEnumerable<string> GetNames() 
      {
          yield return "Qoo";
          yield return "77";
          yield return "Rii";
      }
  }

foreach 區塊

1
2
3
4
5
6
7
static void Main(string[] args)
{
    MyList list = new MyList();
    foreach (var item in list.GetNames()) {
        Console.WriteLine(item);
    }
}

yield return 的工作方式

上面提到了yield return將資料集合按需生成,而不是一次生成整個資料集合。接下來透過一個簡單的範例,我們來看看它的工作方式是什麼樣的,以便加深對它的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void Main(string[] args)
{
    foreach (var num in GetNumbers())
    {
        Console.WriteLine($"外部輸出:{num}");
    }
}

static IEnumerable<int> GetNumbers() {
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine($"內部遍歷了{i}");
        yield return i;
    }
}

輸出結果

首先,在 GetNumbers() 方法中,我們使用yield return關鍵字來定義一個疊代器。這個疊代器可以按需產生整數序列。在每次循環時,使用yield return傳回目前的整數。透過foreach循環來遍歷 GetNumbers() 方法傳回的整數序列。在疊代時 GetNumbers() 方法會被執行,但是不會將整個序列載入到記憶體中。而是在需要時,按需產生序列中的每個元素。在每次迭代時,會輸出目前疊代的整數對應的資訊。所以輸出的結果為:

1
2
3
4
5
6
7
8
9
10
內部遍歷了0
外部輸出:0
內部遍歷了1
外部輸出:1
內部遍歷了2
外部輸出:2
內部遍歷了3
外部輸出:3
內部遍歷了4
外部輸出:4

可以看到,整數序列是按需生成的,並且在每次生成時都會輸出相應的資訊。這種方式可以大大減少記憶體佔用,並且提高程式的效能。

範例

使用yield return 拿到所有的偶數,有結果立即回傳,提供更好的即時性,而不用一次性產生整個資料集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void Main(string[] args)
{
    foreach (var item in GetAllEvenNumber())
    {
        Console.WriteLine(item);//輸出偶數
    }
}

//求1-100的偶數
//使用yield return 拿到所有的偶數
//有結果立即回傳,提供更好的即時性
//而不用一次性產生整個資料集合
static IEnumerable<int> GetAllEvenNumber() 
{
    for (int i = 0; i <= 100; i++)
    {
        if (i % 2 == 0) {
            yield return i;
        }
    }
}

結論

Yield Return關鍵字的作用就是退出目前函數,並且會保存目前函數執行到什麼地方,也就上下文。你會發現下次執行這個函數跟上次跑來的程式碼是不會重複執行的。

但是一般的return result 假如你在循環體提前return,下面調這個函數是會從第一步開始重新執行的。不會記錄上次執行的地方。

yield return 的優勢

善用 yield return 省時省 CPU 省 RAM,打造高效率程式

  • 有結果立即回傳,提供更好的即時性 (這是要串接成生產線模式的必要條件)
  • 只需部分結果時,省去處理無用資料的成本
  • 不需耗用記憶體儲存全部結果

遇到結果筆數龐大或產生資料成本偏高的情境,善用這些優勢,將能打造出更有效率的系統。

MSDN - Yield 陳述式 - 提供下一個元素
善用 yield return 省時省 CPU 省 RAM,打造高效率程式
由C# yield return引发的思考
Book: Visual C# 2005 建構資訊系統實戰經典教本

This post is licensed under CC BY 4.0 by the author.

[C# 筆記] 泛型 (Generics)

[C# 筆記] 匿名方法(Anonymous methods)