Sdílet prostřednictvím


yield statement - provide the next element

yield Příkaz v iterátoru použijte 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.

Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.

Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.

Návod

Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.

V předchozích příkladech je IEnumerable<T>návratový typ iterátorů . V negenerických případech použijte IEnumerable jako návratový typ iterátoru. 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 se příkazy kompilují 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é