yield (справочник по C#)

Использование в операторе yield слова yield означает, что метод, оператор или метод доступа get, в котором присутствует это ключевое слово, является итератором. Использование yield для определения итератора исключает необходимость применения явного дополнительного класса (в котором содержится состояние перечисления; в качестве примера см. IEnumerator<T>) при реализации шаблонов IEnumerable и IEnumerator для пользовательского типа коллекции.

В следующем примере показаны две формы оператора yield.

yield return <expression>;
yield break;

Примечания

Оператор yield return используется для возврата каждого элемента по одному.

Последовательность, которая возвращается после выполнения метода итератора, можно использовать с помощью оператора foreach или запроса LINQ. Каждая итерация цикла foreach вызывает метод итератора. При достижении в методе итератора оператора yield return возвращается expression и сохраняется текущее расположение в коде. При следующем вызове функции итератора выполнение возобновляется с этого места.

Когда итератор возвращает System.Collections.Generic.IAsyncEnumerable<T>, эту последовательность можно использовать асинхронно с помощью оператора System.Collections.Generic.IAsyncEnumerable<T>. Итерация цикла аналогична выполнению инструкции foreach. Разница заключается в том, что каждая итерация может быть приостановлена для асинхронной операции перед возвратом выражения для следующего элемента.

Для завершения итерации можно использовать оператор yield break.

Дополнительные сведения об итераторах см. в разделе Итераторы.

Методы итератора и методы доступа get

Объявление итератора должно соответствовать следующим требованиям.

Тип yield итератора, который возвращает IEnumerable или IEnumerator, — object. Если итератор возвращает IEnumerable<T> или IEnumerator<T>, необходимо выполнить неявное преобразование из типа выражения в операторе yield return в параметр универсального типа.

Нельзя включать инструкцию yield return или yield break:

Обработка исключений

Оператор yield return нельзя размещать в блоке try-catch. Оператор yield return можно размещать в блоке try оператора try-finally.

Оператор yield break можно размещать в блоке try или catch, но не в блоке finally.

Если тело foreach или finally (за пределами метода итератора) вызывает исключение, выполняется блок await foreach в методе итератора.

Техническая реализация

В следующем коде возвращается объект IEnumerable<string> из метода итератора и затем выполняется перебор его элементов.

IEnumerable<string> elements = MyIteratorMethod();
foreach (string element in elements)
{
   ...
}

При вызове MyIteratorMethod тело метода не выполняется. Вместо этого вызов возвращает IEnumerable<string> в переменную elements.

В итерации цикла foreach метод MoveNext вызывается для elements. Этот вызов выполняет тело MyIteratorMethod до достижения следующего оператора yield return. Выражение, возвращаемое оператором yield return, определяет не только значение переменной element для использования телом цикла, но и свойство Current объекта elements, представляющее собой IEnumerable<string>.

В каждой последующей итерации цикла foreach выполнение тела итератора продолжается с места остановки и при достижении оператора yield return оно снова останавливается. Цикл foreach завершается при достижении конца метода итератора или оператора yield break.

В следующем коде возвращается объект IAsyncEnumerable<string> из метода итератора и затем выполняется перебор его элементов.

IAsyncEnumerable<string> elements = MyAsyncIteratorMethod();
await foreach (string element in elements)
{
   // ...
}

В итерации цикла await foreach метод IAsyncEnumerator<T>.MoveNextAsync вызывается для elements. Возврат System.Threading.Tasks.ValueTask<TResult>, выполняемый MoveNext, завершается при достижении следующего оператора yield return.

В каждой последующей итерации цикла await foreach выполнение тела итератора продолжается с места остановки и при достижении оператора yield return оно снова останавливается. Цикл await foreach завершается при достижении конца метода итератора или оператора yield break.

Примеры

В следующем примере имеется оператор yield return, расположенный в цикле for. Каждая итерация тела оператора foreach в методе Main создает вызов функции итератора Power. При каждом вызове функции итератора происходит переход к следующему выполнению оператора yield return, которое осуществляется во время следующей итерации цикла for.

Возвращаемый тип метода итератора — IEnumerable (тип интерфейса итератора). При вызове метода итератора возвращается перечисляемый объект, содержащий степени числа.

public class PowersOf2
{
    static void Main()
    {
        // Display powers of 2 up to the exponent of 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }

    public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
    {
        int result = 1;

        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

    // Output: 2 4 8 16 32 64 128 256
}

В следующем примере демонстрируется метод доступа get, представляющий собой итератор. В этом примере каждый оператор yield return возвращает экземпляр пользовательского класса.

public static class GalaxyClass
{
    public static void ShowGalaxies()
    {
        var theGalaxies = new Galaxies();
        foreach (Galaxy theGalaxy in theGalaxies.NextGalaxy)
        {
            Debug.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears.ToString());
        }
    }

    public class Galaxies
    {

        public System.Collections.Generic.IEnumerable<Galaxy> NextGalaxy
        {
            get
            {
                yield return new Galaxy { Name = "Tadpole", MegaLightYears = 400 };
                yield return new Galaxy { Name = "Pinwheel", MegaLightYears = 25 };
                yield return new Galaxy { Name = "Milky Way", MegaLightYears = 0 };
                yield return new Galaxy { Name = "Andromeda", MegaLightYears = 3 };
            }
        }
    }

    public class Galaxy
    {
        public String Name { get; set; }
        public int MegaLightYears { get; set; }
    }
}

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также