Поделиться через


Оператор 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 нельзя использовать в следующих инструкциях:

Выполнение итератора

Вызов итератора не выполняется немедленно, как показано в следующем примере:

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#.

См. также