Каковы реальные применения yield?

Я знаю, что yield делает, и я видел несколько примеров, но я не могу думать о реальных приложениях, вы использовали его для решения какой-то конкретной проблемы?

(В идеале какая-то проблема, которую другим способом не решить)

Ответов (7)

Решение

Я понимаю, что это старый вопрос (до Джона Скита?), Но я сам обдумывал этот вопрос совсем недавно. К сожалению, текущие ответы здесь (на мой взгляд) не упоминают наиболее очевидное преимущество оператора yield.

Самым большим преимуществом оператора yield является то, что он позволяет вам перебирать очень большие списки с гораздо более эффективным использованием памяти, чем, скажем, при использовании стандартного списка.

Например, предположим, что у вас есть запрос к базе данных, который возвращает 1 миллион строк. Вы можете получить все строки с помощью DataReader и сохранить их в списке, поэтому для этого потребуется list_size * row_size байтов памяти.

Или вы можете использовать оператор yield для создания Iterator и хранить в памяти только одну строку за раз. Фактически это дает вам возможность предоставлять возможность «потоковой передачи» больших наборов данных.

Более того, в коде, который использует Iterator, вы используете простой цикл foreach и можете решить выйти из цикла по мере необходимости. Если вы прерываете работу раньше, вы не принудительно извлекаете весь набор данных, когда вам нужны были только первые 5 строк (например).

Касательно:

Ideally some problem that cannot be solved some other way

Оператор yield не дает вам ничего, что вы не могли бы сделать с помощью собственной реализации итератора, но избавляет вас от необходимости писать часто сложный код. Очень мало проблем (если они есть), которые нельзя решить более чем одним способом.

Вот несколько более свежих вопросов и ответов, которые содержат более подробную информацию:

Добавленная стоимость ключевого слова доходности?

Полезен ли yield за пределами LINQ?

Вы также можете использовать 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);
    }
}

Одно интересное использование - это механизм асинхронного программирования, особенно для задач, которые выполняются в несколько этапов и требуют одного и того же набора данных на каждом этапе. Двумя примерами этого могут быть Jeffery Richters AysncEnumerator Часть 1 и Часть 2 . Среда выполнения с параллелизмом и координацией (CCR) также использует этот метод итераторы CCR .

Операторы LINQ в классе Enumerable реализованы как итераторы, созданные с помощью оператора yield. Он позволяет связывать такие операции, как Select () и Where (), без фактического перечисления чего-либо до тех пор, пока вы на самом деле не используете перечислитель в цикле, обычно с помощью оператора foreach . Кроме того, поскольку при вызове IEnumerator.MoveNext () при вызове IEnumerator.MoveNext () вычисляется только одно значение, если вы решите остановить сбор данных в середине, вы сохраните снижение производительности при вычислении всех результатов.

Итераторы также могут использоваться для реализации других видов ленивых вычислений, когда выражения оцениваются только тогда, когда вам это нужно. Вы также можете использовать 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 час.

Еще одно хорошее использование 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);
}

Использование yield может предотвратить переход к конкретному типу. Это удобно, чтобы гарантировать, что потребитель коллекции не будет ею манипулировать.