Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
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, как показано в следующем примере: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 нельзя использовать в следующих инструкциях:
- методы с параметрами в, refили out.
- лямбда-выражения и анонимные методы.
-
небезопасные блоки. До C# 13
yieldнедопустимо в любом методе с блокомunsafe. Начиная с C# 13, можно использоватьyieldв методах сunsafeблоками, но не в блокеunsafe. -
yield returnиyield breakне может использоваться в блоках catch и , наконец , или в блоках try с соответствующимcatchблоком. Операторыyield returnи инструкции можно использовать в блокеyield breakбезtryблоков, а толькоcatchблок.finally
using операторы в итераторах
Инструкции можно использовать using в методах итератора. Так как using операторы компилируются в try блоки с finally предложениями (и без catch блоков), они работают правильно с итераторами. Удаленные ресурсы правильно управляются во время выполнения итератора:
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.");
}
Как показано в предыдущем примере, ресурс, полученный в using инструкции, остается доступным во время выполнения итератора, даже если итератор приостанавливает и возобновляет выполнение операторов 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#.