Sdílet prostřednictvím


yield statement - provide the next element

Příkaz v iterátoru yield použijete k zadání další hodnoty nebo signálu na konci iterace. Příkaz yield má dvě následující formy:

  • yield return: chcete-li zadat další hodnotu v iteraci, jak ukazuje následující příklad:

    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: explicitně signalizovat konec iterace, jak ukazuje následující příklad:

    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;
            }
        }
    }
    

    Iterace se také dokončí, když ovládací prvek dosáhne konce iterátoru.

V předchozíchpříkladch IEnumerable<T>IEnumerable Můžete také použít IAsyncEnumerable<T> jako návratový typ iterátoru. Díky tomu je iterátor asynchronní. await foreach Příkaz použijte k iteraci výsledku iterátoru, jak ukazuje následující příklad:

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> nebo IEnumerator může být také návratový typ iterátoru. Tyto návratové typy použijte při implementaci GetEnumerator metody v následujících scénářích:

  • Navrhujete typ, který implementuje IEnumerable<T> nebo IEnumerable rozhraní.

  • Přidáním instance nebo povolíte iteraci přes instanci typu pomocí GetEnumerator, jak ukazuje následující příklad:

    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;
        }
    }
    

Příkazy nemůžete použít yield v:

  • metody s v, refnebo out parametry.
  • výrazy lambda a anonymní metody.
  • nebezpečných bloků. Před jazykem C# 13 yield byla v jakékoli metodě s blokem neplatná unsafe . Počínaje jazykem C# 13 můžete používat yield metody s unsafe bloky, ale ne v unsafe bloku.
  • yield return a yield break nelze je použít v blokech catch a finally nebo v blocích try s odpovídajícím catch blokem. Příkazy yield return a příkazy lze použít v yield break bloku bez try bloků, pouze bloku catchfinally.

using příkazy v iterátorech

Příkazy můžete použít using v metodách iterátoru. Vzhledem k tomu, že using příkazy jsou zkompilovány do try bloků s finally klauzulemi (a bez catch bloků), fungují správně s iterátory. Uvolnitelné prostředky se správně spravují během provádění iterátoru:

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.");
}

Jak ukazuje předchozí příklad, prostředek získaný v using příkazu zůstane dostupný během provádění iterátoru, i když iterátor pozastaví a obnoví provádění v yield return příkazech. Prostředek se odstraní po dokončení iterátoru (buď dosažením konce nebo prostřednictvím yield break), nebo když se iterátor sám odstraní (například když se volající vyhne výčtu dříve).

Spuštění iterátoru

Volání iterátoru ho nespustí okamžitě, jak ukazuje následující příklad:

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.

Jak ukazuje předchozí příklad, když začnete iterovat výsledek iterátoru, iterátor se spustí, dokud nedosáhnete prvního yield return příkazu. Pak se spuštění iterátoru pozastaví a volající získá první hodnotu iterace a zpracuje ji. Při každé další iteraci se spuštění iterátoru obnoví po yield return příkazu, který způsobil předchozí pozastavení a pokračuje až do dosažení dalšího yield return příkazu. Iterace se dokončí, když ovládací prvek dosáhne konce iterátoru yield break nebo příkazu.

specifikace jazyka C#

Další informace naleznete v části příkaz výnosu specifikace jazyka C#.

Viz také