Когда вам нужно отложенное выполнение.
Это имеет смысл в большинстве случаев, когда альтернативой является создание временной коллекции.
Рассмотрим такой сценарий: у меня есть список целых чисел, и я хочу перечислить их квадраты.
Я мог бы сделать вот что:
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
для этой цели. Я мог бы использовать 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
, то приведенный выше код выполнял бы потенциально гораздо больше работы, чем нужно.
Когда вам лень писать свой собственный IEnumerator ;)
Со страницы 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
фактически "приостанавливается" после каждой итерации, чтобы дать возможность вызывающей программе выполнить какое-то действие. В данном случае распечатать результат.
См. в этой статье .
Yield действует как заполнитель возврата - это нелокальная точка возврата goto, которая сохраняет среду метода и позволяет коду «возвращаться» обратно. Подобным образом (в инвертированном виде) передача делегата в метод, который позволяет вам внедрять определенную логику в другой метод, замыкания позволяют выполнять различные типы работы «вокруг» более общего метода, позволяя сохранить небольшой размер кода, модульность и возможность повторного использования.
Это могло бы сделать код намного более эффективным. Вместо создания очень большой коллекции можно было бы разрешить последовательное действие с отдельными объектами (и они удаляются после каждой операции). Я полагаю, вы могли бы создать случаи, когда простой итератор было бы чрезвычайно сложно построить.