yield-instruktion – ange nästa element

Du använder -instruktionen yield i en iterator för att ange nästa värde eller signalera slutet på en iteration. -instruktionen yield har följande två formulär:

  • yield return: för att ange nästa värde i iterationen, som följande exempel visar:

    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: för att uttryckligen signalera slutet på iterationen, som följande exempel visar:

    Console.WriteLine(string.Join(" ", TakeWhilePositive([2, 3, 4, 5, -1, 3, 4])));
    // Output: 2 3 4 5
    
    Console.WriteLine(string.Join(" ", TakeWhilePositive([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;
            }
        }
    }
    

    Iterationen avslutas också när kontrollen når slutet av en iterator.

I föregående exempel är IEnumerable<T> returtypen av iteratorer (i icke-generiska fall använder IEnumerable du som returtyp för en iterator). Du kan också använda IAsyncEnumerable<T> som returtyp för en iterator. Det gör en iterator asynkron. Använd -instruktionen await foreach för att iterera över iteratorns resultat, som följande exempel visar:

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> eller IEnumerator kan också vara returtypen för en iterator. Det är användbart när du implementerar GetEnumerator metoden i följande scenarier:

  • Du utformar den typ som implementerar IEnumerable<T> eller IEnumerable gränssnitt.

  • Du lägger till en instans- eller tilläggsmetodGetEnumerator för att aktivera iteration över typens instans med -instruktionenforeach, som följande exempel visar:

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

Du kan inte använda -uttrycken yield i:

Körning av en iterator

Anropet av en iterator kör det inte omedelbart, vilket visas i följande exempel:

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.

Som föregående exempel visar, när du börjar iterera över en iterators resultat, körs en iterator tills den första yield return instruktionen har nåtts. Sedan pausas körningen av en iterator och anroparen får det första iterationsvärdet och bearbetar det. På varje efterföljande iteration återupptas körningen av en iterator efter -instruktionen yield return som orsakade den tidigare avstängningen och fortsätter tills nästa yield return instruktion har nåtts. Iterationen slutförs när kontrollen når slutet av en iterator eller en yield break -instruktion.

Språkspecifikation för C#

Mer information finns i avsnittet yield statement i C#-språkspecifikationen.

Se även