Delen via


rendementsinstructie - geef het volgende element op

U gebruikt de yield instructie in een iterator om de volgende waarde op te geven of het einde van een iteratie aan te geven. De yield instructie heeft de twee volgende formulieren:

  • yield return: om de volgende waarde in iteratie op te geven, zoals in het volgende voorbeeld wordt weergegeven:

    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: om het einde van de iteratie expliciet aan te geven, zoals in het volgende voorbeeld wordt weergegeven:

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

    Iteratie wordt ook voltooid wanneer het besturingselement het einde van een iterator bereikt.

In de voorgaande voorbeelden is IEnumerable<T> het retourtype iterators (in niet-generische gevallen gebruikt IEnumerable u als retourtype van een iterator). U kunt ook als retourtype van een iterator gebruiken IAsyncEnumerable<T> . Dat maakt een iterator asynchroon. Gebruik de await foreach instructie om het resultaat van iterator te herhalen, zoals in het volgende voorbeeld wordt weergegeven:

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> of IEnumerator kan ook het retourtype van een iterator zijn. Gebruik deze retourtypen wanneer u de GetEnumerator methode in de volgende scenario's implementeert:

  • U ontwerpt het type dat implementeert IEnumerable<T> of IEnumerable interface implementeert.

  • U voegt een exemplaar- of toe om iteratie in te schakelen voor het exemplaar van het type met de GetEnumerator, zoals in het volgende voorbeeld wordt weergegeven:

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

U kunt de yield instructies niet gebruiken in:

  • methoden met in, ref, of uit parameters.
  • lambda-expressies en anonieme methoden.
  • onveilige blokken. Vóór C# 13 was yield deze ongeldig in een methode met een unsafe blok. Vanaf C# 13 kunt yield u gebruiken in methoden met unsafe blokken, maar niet in het unsafe blok.
  • yield return en yield break kan niet worden gebruikt in catch en ten slotte blokken, of in try-blokken met een corresponderend catch blok. De yield return en yield break instructies kunnen worden gebruikt in een try blok zonder catch blokken, alleen een finally blok.

using instructies in iterators

U kunt instructies gebruiken using in iteratormethoden. Omdat using instructies worden gecompileerd in try blokken met finally componenten (en geen catch blokken), werken ze correct met iterators. De wegwerpresources worden correct beheerd tijdens de uitvoering van de iterator:

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

Zoals in het voorgaande voorbeeld wordt weergegeven, blijft de resource die in de using instructie is verkregen beschikbaar gedurende de uitvoering van de iterator, zelfs wanneer de iterator de uitvoering bij yield return instructies onderbreekt en hervat. De resource wordt verwijderd wanneer de iterator is voltooid (ofwel door het einde of via yield break) te bereiken of wanneer de iterator zelf wordt verwijderd (bijvoorbeeld wanneer de aanroeper vroeg uit opsomming uitvalt).

Uitvoering van een iterator

De aanroep van een iterator wordt niet onmiddellijk uitgevoerd, zoals in het volgende voorbeeld wordt weergegeven:

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.

Zoals in het voorgaande voorbeeld wordt weergegeven, wordt een iterator uitgevoerd wanneer u het resultaat van een iterator begint te herhalen totdat de eerste yield return instructie is bereikt. Vervolgens wordt de uitvoering van een iterator onderbroken en krijgt de aanroeper de eerste iteratiewaarde en verwerkt deze. Bij elke volgende iteratie wordt de uitvoering van een iterator hervat na de instructie die de yield return vorige schorsing heeft veroorzaakt en gaat door totdat de volgende yield return instructie is bereikt. De iteratie wordt voltooid wanneer het besturingselement het einde van een iterator of een yield break instructie bereikt.

C#-taalspecificatie

Zie de sectie Rendementsinstructie van de C#-taalspecificatie voor meer informatie.

Zie ook