Можно создать дубликат:
C# - Proper Use of yield returnКогда «Урожай» действительно нужен?
Что может быть реальным примером использования C# выход?
Спасибо.
Можно создать дубликат:
C# - Proper Use of yield returnКогда «Урожай» действительно нужен?
Что может быть реальным примером использования C# выход?
Спасибо.
Если вы хотите deferred execution.
Это имеет смысл в большинстве случаев, когда альтернатива заключается в создании временной коллекции.
Рассмотрите этот сценарий: у меня есть список целых чисел, и я хочу перечислить их квадраты.
Я мог бы сделать это:
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>
перед возвращением, приведенный выше код будет делать потенциально гораздо больше работы, чем это необходимо.
Хорошо написанный пример. + 1, почему разница может быть не очевидной. – Paddy
Используется в блоке итератора, чтобы обеспечить значение объекта нумератора или сигнал об окончании итерации.
Вы используете его при создании пользовательского итератора. Используя пример со страницы:
// 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
эффективно «паузу» после каждой итерации, чтобы процедура вызова выполнить какое-либо действие. В этом случае распечатайте результат.
Когда вы слишком ленивы, чтобы написать свой собственный IEnumerator;)
+1 Я думаю, что я всегда слишком ленив ... –
И, наверное, следует отметить, что существует разница между IEnumerator и IEnumerable, слова пишется аналогично достаточно, чтобы меня путали, потому что я читал разные определения для того, что я мысль был то же самое – Davy8
@ Davy8 очень правда! Разница в том, что многие люди путаются. IEnumerator содержит всю логику, скрытую за урожаем; IEnumerable просто предоставит доступ к IEnumerator. –
См this article.
Выход действует как возвращаемый-placeholder - это нелокальная точка возврата goto, которая сохраняет среду метода и позволяет коду «прыгать» обратно.В некотором роде аналогичный (вид перевернутого) для передачи делегата в метод, который позволяет вам вводить определенную логику в рамках другого метода, закрытие позволяет вам выполнять различные типы работы «вокруг» более общего метода, позволяя вам сохранять код небольшим и модульные и повторно используемые.
Это может привести к значительно более эффективному коду. Вместо создания экземпляра очень большой коллекции можно было бы позволить отдельным объектам действовать последовательно (и они отбрасываются после каждой операции). Я предполагаю, что вы могли бы построить случаи, когда простой итератор было бы чрезвычайно сложно построить.
Justin Etheredge объясняет это действительно красиво в серии TekPub Mastering LINQ. http://tekpub.com/preview/linq – jessegavin