您在yield中使用 陳述式來提供下一個值,或發出反覆運算結束的訊號。
yield 陳述式具有下列兩種形式:
yield return:在反覆運算中提供下一個值,如下列範例所示:foreach (int i in ProduceEvenNumbers(9)) { Console.Write(i); Console.Write(" "); } // Output: 0 2 4 6 8 IEnumerable<int> ProduceEvenNumbers(int upto) { for (int i = 0; i <= upto; i += 2) { yield return i; } }yield break:明確發出反覆運算結束的訊號,如下列範例所示:Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {2, 3, 4, 5, -1, 3, 4}))); // Output: 2 3 4 5 Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {9, 8, 7}))); // Output: 9 8 7 IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers) { foreach (int n in numbers) { if (n > 0) { yield return n; } else { yield break; } } }當控制權觸達迭代器的結尾時,反覆運算也會完成。
在上述範例中,迭代器的傳回類型為 IEnumerable<T> (在非泛型案例中,使用 IEnumerable 作為迭代器的傳回型別)。 您也可以使用 IAsyncEnumerable<T> 作為迭代器的傳回型別。 那樣可使迭代器進行非同步處理。 使用 await foreach 陳述式逐一查看迭代器的結果,如下列範例所示:
await foreach (int n in GenerateNumbersAsync(5))
{
Console.Write(n);
Console.Write(" ");
}
// Output: 0 2 4 6 8
async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
for (int i = 0; i < count; i++)
{
yield return await ProduceNumberAsync(i);
}
}
async Task<int> ProduceNumberAsync(int seed)
{
await Task.Delay(1000);
return 2 * seed;
}
IEnumerator<T> 或 IEnumerator 也可以是迭代器的傳回類型。 在下列案例中實作 GetEnumerator 方法時,請使用這些傳回型別:
您可以設計實作 IEnumerable<T> 或 IEnumerable 介面的類型。
您可以新增執行個體或擴充
GetEnumerator方法,以使用foreach陳述式在類型的執行個體上啟用反覆運算,如下列範例所示:public static void Example() { var point = new Point(1, 2, 3); foreach (int coordinate in point) { Console.Write(coordinate); Console.Write(" "); } // Output: 1 2 3 } public readonly record struct Point(int X, int Y, int Z) { public IEnumerator<int> GetEnumerator() { yield return X; yield return Y; yield return Z; } }
您無法在下列內容中使用 yield 陳述式:
- 具有 的方法位於、ref或 out 參數中。
- lambda 運算式 和 匿名方法。
-
不安全的區塊。 在 C# 13 之前,
yield在具有unsafe區塊的任何方法中都無效。 從 C# 13 開始,可以在包含yield區塊的方法中使用unsafe,但無法在unsafe區塊中使用。 -
yield returnyield break且不能用於捕捉區塊和最終區塊,或是嘗試區塊與對應catch區塊。yield return和yield break語句可以用於沒有try區塊的區塊中catch,只有finally區塊。
using 迭代器中的陳述式
您可以在迭代器方法中使用陳述式。using 由於語句被編譯成帶有子句(沒有塊using)的try塊,finally因此catch它們可以與迭代器正常工作。 在迭代器執行過程中,可處置資源會得到妥善管理:
Console.WriteLine("=== Using in Iterator Example ===");
// Demonstrate that using statements work correctly in iterators
foreach (string line in ReadLinesFromResource())
{
Console.WriteLine($"Read: {line}");
// Simulate processing only first two items
if (line == "Line 2") break;
}
Console.WriteLine("Iteration stopped early - resource should still be disposed.");
static IEnumerable<string> ReadLinesFromResource()
{
Console.WriteLine("Opening resource...");
using var resource = new StringWriter(); // Use StringWriter as a simple IDisposable
resource.WriteLine("Resource initialized");
// These lines would typically come from the resource (e.g., file, database)
string[] lines = { "Line 1", "Line 2", "Line 3", "Line 4" };
foreach (string line in lines)
{
Console.WriteLine($"About to yield: {line}");
yield return line;
Console.WriteLine($"Resumed after yielding: {line}");
}
Console.WriteLine("Iterator completed - using block will dispose resource.");
}
如上述範例所示,在陳述式中 using 取得的資源在整個反覆運算器的執行期間仍可供使用,即使反覆運算器在陳述式暫停 yield return 並繼續執行也一樣。 當反覆運算器完成時,資源會處置 (到達結尾或透過 yield break) ,或當反覆運算器本身處置時 (例如,當呼叫端提前中斷列舉時) 。
執行迭代器
呼叫迭代器不會立即執行,如下列範例所示:
var numbers = ProduceEvenNumbers(5);
Console.WriteLine("Caller: about to iterate.");
foreach (int i in numbers)
{
Console.WriteLine($"Caller: {i}");
}
IEnumerable<int> ProduceEvenNumbers(int upto)
{
Console.WriteLine("Iterator: start.");
for (int i = 0; i <= upto; i += 2)
{
Console.WriteLine($"Iterator: about to yield {i}");
yield return i;
Console.WriteLine($"Iterator: yielded {i}");
}
Console.WriteLine("Iterator: end.");
}
// Output:
// Caller: about to iterate.
// Iterator: start.
// Iterator: about to yield 0
// Caller: 0
// Iterator: yielded 0
// Iterator: about to yield 2
// Caller: 2
// Iterator: yielded 2
// Iterator: about to yield 4
// Caller: 4
// Iterator: yielded 4
// Iterator: end.
如上述範例所示,當您開始逐一查看迭代器的結果時,即會執行迭代器,直到觸達第一個 yield return 陳述式為止。 接著,迭代器的執行會暫止,而呼叫者會取得第一個反覆運算值並加以處理。 在後續的每次反覆運算中,迭代器的執行會在導致先前暫止的 yield return 陳述式之後繼續,並繼續進行,直到觸達下一個 yield return 陳述式為止。 當控制權觸達迭代器的結尾或 yield break 陳述式時,反覆運算就會完成。
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的 yield 陳述式一節。