Я знаю, что yield
значит, и я видел несколько примеров, но я не могу придумать реальных приложений, использовали ли вы их для решения какой-то конкретной проблемы?
(В идеале какая-то проблема, которая не может быть решена другим способом)
Я понимаю, что это старый вопрос (предварительно Джон Скит?), Но я сам недавно обдумывал этот вопрос. К сожалению, текущие ответы здесь (на мой взгляд) не упоминают о наиболее очевидном преимуществе оператора yield.
Самым большим преимуществом оператора yield является то, что он позволяет перебирать очень большие списки с гораздо более эффективным использованием памяти, чем при использовании, скажем, стандартного списка.
Например, предположим, у вас есть запрос к базе данных, который возвращает 1 миллион строк. Вы можете извлечь все строки, используя DataReader, и сохранить их в списке, поэтому требуются байты памяти list_size * row_size.
Или вы можете использовать оператор yield для создания Iterator и хранить только одну строку в памяти за раз. По сути, это дает вам возможность обеспечить потоковую передачу больших массивов данных.
Более того, в коде, который использует Iterator, вы используете простой цикл foreach и можете решить выйти из цикла по мере необходимости. Если вы делаете перерыв рано, вы не принудительно извлекаете весь набор данных, когда вам нужны только первые 5 строк (например).
Что касается:
Ideally some problem that cannot be solved some other way
Оператор yield не дает вам ничего, что вы не могли бы сделать с помощью собственной пользовательской реализации итератора, но оно избавляет вас от необходимости писать часто сложный необходимый код. Есть очень мало проблем (если таковые имеются), которые не могут быть решены более чем одним способом.
Вот пара более свежих вопросов и ответов, которые предоставляют более подробную информацию:
на самом деле я использую его нетрадиционным способом на моем сайте IdeaPipe
public override IEnumerator<T> GetEnumerator()
{
// goes through the collection and only returns the ones that are visible for the current user
// this is done at this level instead of the display level so that ideas do not bleed through
// on services
foreach (T idea in InternalCollection)
if (idea.IsViewingAuthorized)
yield return idea;
}
так что в основном он проверяет, разрешено ли в данный момент просматривать идею, и возвращает ли она идею. Если это не так, это просто пропущено. Это позволяет мне кэшировать идеи, но по-прежнему отображать идеи для авторизованных пользователей. В противном случае мне придется повторять их каждый раз в зависимости от разрешений, когда они переоцениваются только каждые 1 час.
Операторы LINQ в классе Enumerable реализованы как итераторы, которые создаются с помощью оператора yield. Это позволяет вам связывать такие операции, как Select () и Where (), фактически ничего не перечисляя, пока вы фактически не используете перечислитель в цикле, обычно с помощью оператора foreach . Кроме того, поскольку при вызове IEnumerator.MoveNext () вычисляется только одно значение, если вы решите остановить сбор данных в середине, вы сохраните снижение производительности при вычислении всех результатов.
Итераторы также могут быть использованы для реализации других видов отложенной оценки, когда выражения оцениваются только тогда, когда вам это нужно. Вы также можете использовать yield для более модных вещей, таких как сопрограммы.
Другое хорошее использование для yield - это выполнение функции над элементами IEnumerable и возвращение результата другого типа, например:
public delegate T SomeDelegate(K obj);
public IEnumerable<T> DoActionOnList(IEnumerable<K> list, SomeDelegate action)
{
foreach (var i in list)
yield return action(i);
}
Одно интересное использование - это механизм асинхронного программирования, особенно для задач, которые выполняют несколько шагов и требуют одинаковый набор данных на каждом шаге. Два примера этого были бы Джеффри Рихтерс AysncEnumerator Часть 1 и Часть 2 . Среда параллелизма и координации (CCR) также использует эту технику итераторов CCR .
Использование выхода может предотвратить снижение до конкретного типа. Это удобно для того, чтобы потребитель коллекции не манипулировал ею.
Вы также можете использовать yield return
для обработки серии результатов функции в виде списка. Например, рассмотрим компанию, которая платит своим сотрудникам каждые две недели. Можно получить подмножество дат заработной платы в виде списка, используя этот код:
void Main()
{
var StartDate = DateTime.Parse("01/01/2013");
var EndDate = DateTime.Parse("06/30/2013");
foreach (var d in GetPayrollDates(StartDate, EndDate)) {
Console.WriteLine(d);
}
}
// Calculate payroll dates in the given range.
// Assumes the first date given is a payroll date.
IEnumerable<DateTime> GetPayrollDates(DateTime startDate, DateTime endDate, int daysInPeriod = 14) {
var thisDate = startDate;
while (thisDate < endDate) {
yield return thisDate;
thisDate = thisDate.AddDays(daysInPeriod);
}
}