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(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; } } }
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änds IEnumerable 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. Använd dessa returtyper 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äggsmetod
GetEnumerator
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:
- metoder med parametrarna in, ref eller out
- lambda-uttryck och anonyma metoder
- osäkra block. Före C# 13
yield
var ogiltigt i alla metoder med ettunsafe
block. Från och med C# 13 kan du användayield
i metoder medunsafe
block, men inte iunsafe
blocket. yield return
ochyield break
kan inte användas i försök, fånga och slutligen block.
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.