C# yield 執行流程 開始 迭代器狀態機 呼叫 MoveNext() yield return 值 保存目前狀態 # 為什麼需要 yield?

在C#開發中,我們經常需要處理大量數據的集合。傳統的做法是先將所有數據載入到記憶體中,再進行處理。但這種方式在處理大量數據時會佔用大量記憶體,影響程式效能。

這時候就需要用到 yield 關鍵字。yield 可以讓我們實現延遲執行(Lazy Evaluation),也就是說,只有在真正需要數據時才進行處理,大大節省記憶體使用。

yield 基本用法

讓我們從一個簡單的例子開始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用 yield return 產生費波那契數列
public static IEnumerable<int> Fibonacci(int count)
{
int current = 0;
int next = 1;

// 產生指定數量的費波那契數
for (int i = 0; i < count; i++)
{
yield return current; // 返回當前數字
int temp = next;
next = current + next;
current = temp;
}
}

// 使用方式
foreach (int number in Fibonacci(10))
{
Console.WriteLine(number);
}

在這個例子中,yield return 會在每次迭代時返回一個數字,而不是一次性計算出所有數字。這意味著即使我們要產生一百萬個費波那契數,程式也不會一次佔用大量記憶體。

yield break 的使用

有時候我們需要在某個條件下提前結束迭代,這時可以使用 yield break:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 讀取文件直到遇到空行
public static IEnumerable<string> ReadUntilEmptyLine(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrEmpty(line))
{
yield break; // 遇到空行就結束迭代
}
yield return line;
}
}
}

yield 的應用場景

  1. 大文件處理
  2. 數據庫分頁查詢
  3. 自定義集合類別
  4. 無限序列生成

以數據庫分頁查詢為例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static IEnumerable<User> GetUsersByPage(int pageSize)
{
int offset = 0;
while (true)
{
// 每次只查詢一頁的數據
var users = dbContext.Users
.Skip(offset)
.Take(pageSize)
.ToList();

if (!users.Any())
{
yield break;
}

foreach (var user in users)
{
yield return user;
}

offset += pageSize;
}
}

最佳實踐與注意事項

  1. yield 方法內不能使用 try-catch-finally
  2. yield return 只能在方法內直接使用
  3. yield 方法的返回類型必須是 IEnumerable 或相關介面
  4. 避免在 yield return 中執行耗時操作

結論

yield 是C#中非常強大的功能,能夠幫助我們編寫更高效的程式碼。通過延遲執行的特性,我們可以更好地控制記憶體使用,提升程式效能。

要進一步了解 yield 的使用,可以參考: