共用方式為


Yield 陳述式 - 提供下一個元素

您在迭代器中使用 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 陳述式:

  • 具有 inrefout 參數的方法
  • Lambda 運算式匿名方法
  • 不安全的區塊。 在 C# 13 之前,yield 在具有 unsafe 區塊的任何方法中都無效。 從 C# 13 開始,可以在包含 unsafe 區塊的方法中使用 yield,但無法在 unsafe 區塊中使用。
  • yield returnyield break 不能用於 catchfinally 區塊,或在 try 區塊中使用對應的catch區塊。 yield returnyield break 語句可以用於沒有catch區塊的區塊中try,只有finally區塊。

執行迭代器

呼叫迭代器不會立即執行,如下列範例所示:

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 陳述式一節。

另請參閱