Ответ 1

Если вы хотите отложенное выполнение.

Это имеет смысл в большинстве случаев, когда альтернативой является создание временной коллекции.

Рассмотрим этот сценарий: у меня есть список целых чисел, и я хочу перечислить их квадраты.

Я мог бы сделать это:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    List<int> squares = new List<int>();
    foreach (int number in numbers)
        squares.Add(number * number);

    return squares;
}

Тогда я мог бы суммировать квадраты, взять их среднее, найти наибольшее и т.д.

Но мне действительно не нужно было заполнять целую новую List<int> для этой цели. Я мог бы использовать yield для перечисления по первому списку и возврата квадратов один за другим:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    foreach (int number in numbers)
        yield return number * number;
}

Тот факт, что это действительно имеет значение, может быть не очевидным, пока вы не начнете иметь дело с очень большими коллекциями, где заполнение временных коллекций окажется довольно расточительным.

Например, предположим, что я хотел найти первый квадрат над определенным порогом. Я мог бы сделать это:

IEnumerable<int> numbers = GetLotsOfNumbers();
var squares = numbers.Squares();
int firstBigSquare = squares
    .Where(x => x >= 1000)
    .FirstOrDefault();

Но если мой метод Squares заполнил весь List<int> перед возвратом, вышеуказанный код будет делать гораздо больше работы, чем необходимо.

Ответ 2

На странице MSDN на yield:

Используется в блоке итератора для предоставления значения объекту перечислителя или для указания конца итерации.

Вы используете его при создании пользовательского итератора. Используя пример со страницы:

// yield-example.cs
using System;
using System.Collections;
public class List
{
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

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

yield означает, что цикл while внутри Power эффективно "приостанавливается" после каждой итерации, чтобы позволить вызывающей процедуре выполнять какое-либо действие. В этом случае распечатайте результат.

Ответ 3

Когда вам слишком лениво написать свой собственный IEnumerator;)

Ответ 4

См. в этой статье.

Выход действует как возвращаемый-placeholder - это нелокальная точка возврата goto, которая сохраняет среду метода и позволяет коду "прыгать" обратно. Подобным образом (тип инвертированного) передается делегат в метод, который позволяет вам вводить определенную логику в рамках другого метода, закрытие позволяет вам выполнять различные типы работы "вокруг" более общего метода, позволяя сохранить код небольшим, модульным и повторно используемым.

Это может сделать гораздо более эффективный код. Вместо создания экземпляра очень большой коллекции можно было бы позволить отдельным объектам действовать последовательно (и они отбрасываются после каждой операции). Я предполагаю, что вы могли бы построить случаи, когда простой итератор было бы чрезвычайно сложно построить.