Оператор yield — укажите следующий элемент
yield
Оператор используется в итераторе для предоставления следующего значения или сигнала о завершении итерации. Инструкция yield
имеет две следующие формы:
yield return
: чтобы указать следующее значение в итерации, как показано в следующем примере: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
: для явного сигнала о завершении итерации, как показано в следующем примере: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; } } }
Итерация также завершается, когда элемент управления достигает конца итератора.
В предыдущих примерах возвращаемый тип итераторов (в негенерических случаях используется IEnumerable<T> IEnumerable в качестве возвращаемого типа итератора). Можно также использовать IAsyncEnumerable<T> в качестве возвращаемого типа итератора. Это делает асинхронный итератор. Используйте инструкцию await foreach
для итерации результатов итератора, как показано в следующем примере:
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> или IEnumerator также может быть типом возвращаемого итератора. Используйте эти типы возвращаемых данных при реализации GetEnumerator
метода в следующих сценариях:
Вы разрабатываете тип, реализующий IEnumerable<T> или IEnumerable интерфейс.
Вы добавляете метод экземпляра или расширения
GetEnumerator
, чтобы включить итерацию экземпляра типа сforeach
помощью инструкции, как показано в следующем примере: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; } }
Инструкции yield
нельзя использовать в следующих инструкциях:
- методы с параметрами in, ref или out
- лямбда-выражения и анонимные методы
- небезопасные блоки. До C# 13
yield
недопустимо в любом методе с блокомunsafe
. Начиная с C# 13, можно использоватьyield
в методах сunsafe
блоками, но не в блокеunsafe
. yield return
иyield break
не может использоваться в попытках, перехвате и наконец блоках.
Выполнение итератора
Вызов итератора не выполняется немедленно, как показано в следующем примере:
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.
Как показано в предыдущем примере, при запуске итерации по результату итератора выполняется итератор до достижения первой yield return
инструкции. Затем выполнение итератора приостановлено, и вызывающий объект получает первое значение итерации и обрабатывает его. При каждой последующей итерации выполнение итератора возобновляется после yield return
инструкции, вызвавшей предыдущую приостановку и продолжающуюся до достижения следующей yield return
инструкции. Итерация завершается, когда элемент управления достигает конца итератора или yield break
оператора.
Спецификация языка C#
Дополнительные сведения см. в разделе инструкции о выходе спецификации языка C#.