Выход ключевого слова является одним из тех ключевых слов в C # , который продолжает мистифицировать меня, и я никогда не был уверен , что я правильно его использовать.
Из следующих двух частей кода, который является предпочтительным и почему?
Версия 1: Использование возврата доходности
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
Версия 2: вернуть список
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
c#
yield-return
senfo
источник
источник
yield
привязанIEnumerable<T>
и к своему виду. Это в некотором роде ленивая оценкаyield return
если код, который перебирает результаты,GetAllProducts()
дает пользователю возможность преждевременно отменить обработку.Ответы:
Я склонен использовать доходность при вычислении следующего элемента в списке (или даже следующей группы элементов).
Используя вашу версию 2, вы должны иметь полный список, прежде чем вернуться. Используя yield-return, вам действительно нужно иметь только следующий товар перед возвратом.
Помимо прочего, это помогает распределить вычислительные затраты на сложные вычисления в течение более длительного периода времени. Например, если список подключен к графическому интерфейсу, и пользователь никогда не переходит на последнюю страницу, вы никогда не вычисляете конечные элементы в списке.
Другой случай, когда доходность-возврат предпочтительнее, это когда IEnumerable представляет бесконечное множество. Рассмотрим список простых чисел или бесконечный список случайных чисел. Вы никогда не можете вернуть полный IEnumerable сразу, поэтому вы используете yield-return для постепенного возврата списка.
В вашем конкретном примере у вас есть полный список продуктов, поэтому я бы использовал версию 2.
источник
Yield return
Кажется, это сокращение для написания вашего собственного класса итератора (реализовать IEnumerator). Следовательно, упомянутые преимущества также применимы к пользовательским классам итераторов. Во всяком случае, обе конструкции сохраняют промежуточное состояние. В его самой простой форме речь идет об удержании ссылки на текущий объект.Заполнение временного списка похоже на загрузку всего видео, а использование
yield
- на потоковое видео.источник
В качестве концептуального примера для понимания того, когда следует использовать
yield
, скажем, методConsumeLoop()
обрабатывает элементы, возвращаемые / получаемые с помощьюProduceList()
:Без
yield
этого вызовProduceList()
может занять много времени, потому что вы должны заполнить список перед возвратом:Используя
yield
, он становится переставленным, работает как бы «параллельно»:И, наконец, как уже предлагали многие, вы должны использовать версию 2, потому что у вас уже есть заполненный список.
источник
Я знаю, что это старый вопрос, но я хотел бы предложить один пример того, как можно творчески использовать ключевое слово yield. Я действительно извлек выгоду из этой техники. Надеюсь, это поможет любому, кто наткнется на этот вопрос.
Примечание: не думайте о ключевом слове yield как о другом способе создания коллекции. Большая часть мощности yield заключается в том, что выполнение в вашем методе или свойстве приостанавливается до тех пор, пока вызывающий код не выполнит итерацию следующего значения. Вот мой пример:
Использование ключевого слова yield (наряду с реализацией сопрограмм Caliburn.Micro Роба Айзенбурга ) позволяет мне выразить асинхронный вызов веб-службы следующим образом:
Для этого нужно включить мой BusyIndicator, вызвать метод Login в моей веб-службе, установить для моего флага IsLoggedIn возвращаемое значение, а затем снова отключить BusyIndicator.
Вот как это работает: IResult имеет метод Execute и событие Completed. Caliburn.Micro извлекает IEnumerator из вызова HandleButtonClick () и передает его в метод Coroutine.BeginExecute. Метод BeginExecute начинает перебирать IResults. Когда возвращается первый IResult, выполнение приостанавливается внутри HandleButtonClick (), и BeginExecute () присоединяет обработчик события к событию Completed и вызывает Execute (). IResult.Execute () может выполнять либо синхронную, либо асинхронную задачу и запускает событие Completed, когда оно выполнено.
LoginResult выглядит примерно так:
Это может помочь настроить что-то вроде этого и пройти через выполнение, чтобы посмотреть, что происходит.
Надеюсь, это поможет кому-то! Я действительно любил исследовать различные способы, которыми можно использовать урожай.
источник
yield
таким образом. Это похоже на элегантный способ эмулировать шаблон асинхронного ожидания / ожидания (который, я полагаю, будет использоваться вместо того,yield
чтобы переписать его сегодня). Считаете ли вы, что это творческое использованиеyield
принесло (без каламбура) убывающую отдачу в течение многих лет по мере развития C # с тех пор, как вы ответили на этот вопрос? Или вы все еще придумываете модернизированные умные варианты использования, такие как эта? И если да, не могли бы вы поделиться с нами еще одним интересным сценарием?Это может показаться странным предложением, но я узнал, как использовать
yield
ключевое слово в C #, прочитав презентацию о генераторах в Python: http://www.dabeaz.com/generators/Generators.pdf Дэвида М. Бизли . Вам не нужно много знать Python, чтобы понять презентацию - я не знал. Мне было очень полезно объяснить не только, как работают генераторы, но и почему вы должны заботиться.источник
Возврат доходности может быть очень мощным для алгоритмов, где вам нужно перебирать миллионы объектов. Рассмотрим следующий пример, где вам нужно рассчитать возможные поездки для совместного использования поездки. Сначала мы генерируем возможные поездки:
Затем повторяйте каждую поездку:
Если вы используете List вместо yield, вам понадобится выделить 1 миллион объектов в память (~ 190 МБ), и для этого простого примера потребуется ~ 1400 мс. Однако, если вы используете yield, вам не нужно помещать все эти временные объекты в память, и вы получите значительно более высокую скорость алгоритма: этот пример займет всего ~ 400 мс для запуска без использования памяти вообще.
источник
yield
работает под прикрытием, внедряя конечный автомат внутри. Вот SO ответ с 3 подробными сообщениями в блоге MSDN, которые объясняют реализацию очень подробно. Автор Раймонд Чен @ MSFTДва куска кода действительно делают две разные вещи. Первая версия будет тянуть участников по мере необходимости. Вторая версия загрузит все результаты в память, прежде чем вы начнете что-либо делать с ней.
Там нет правильного или неправильного ответа на этот. Какой из них предпочтительнее, зависит только от ситуации. Например, если у вас есть ограничение по времени для выполнения запроса, и вам нужно сделать что-то сложное с результатами, вторая версия может быть предпочтительнее. Но остерегайтесь больших наборов результатов, особенно если вы запускаете этот код в 32-битном режиме. Я был укушен исключениями OutOfMemory несколько раз при выполнении этого метода.
Главное, что нужно иметь в виду, это: различия в эффективности. Таким образом, вы, вероятно, должны следовать тому, что делает ваш код проще, и изменять его только после профилирования.
источник
У выхода есть два отличных применения
Это помогает обеспечить пользовательскую итерацию без создания временных коллекций. (загрузка всех данных и цикл)
Это помогает делать итерацию с учетом состояния. (потоковое)
Ниже приведено простое видео, которое я создал с полной демонстрацией, чтобы поддержать два вышеуказанных пункта.
http://www.youtube.com/watch?v=4fju3xcm21M
источник
Вот что говорит Крис Селлс об этих утверждениях на языке программирования C # ;
источник
F().Any()
- он вернется после попытки перечислить только первый результат. В общем, вы не должны полагаться наIEnumerable yield
изменение состояния программы, потому что оно может фактически неПредполагая, что ваши продукты LINQ класс использует аналогичный выход для перечисления / итерации, первая версия более эффективна, потому что она дает только одно значение каждый раз, когда повторяется.
Второй пример - преобразование перечислителя / итератора в список с помощью метода ToList (). Это означает, что он вручную перебирает все элементы в перечислителе, а затем возвращает плоский список.
источник
Это своего рода помимо сути, но так как вопрос помечен как лучшие практики, я добавлю два своих цента. Для такого типа вещей я предпочитаю превратить его в свойство:
Конечно, это немного больше, но код, который использует это, будет выглядеть намного чище:
против
Примечание: я бы не стал делать это для любых методов, которые могут занять некоторое время, чтобы выполнить свою работу.
источник
И что по этому поводу?
Я думаю, это намного чище. У меня нет VS2008 под рукой, чтобы проверить, хотя. В любом случае, если Products реализует IEnumerable (как кажется - он используется в операторе foreach), я бы вернул его напрямую.
источник
Я бы использовал версию 2 кода в этом случае. Поскольку у вас есть полный список доступных продуктов, и это то, что ожидает «потребитель» этого вызова метода, потребуется отправить полную информацию обратно вызывающей стороне.
Если вызывающая сторона этого метода требует «одну» информацию за раз, и потребление следующей информации основано на требовании, тогда было бы полезно использовать возвращение доходности, которое будет гарантировать, что команда выполнения будет возвращена вызывающей стороне, когда единица информации доступна.
Некоторые примеры, где можно использовать возвращение доходности:
Чтобы ответить на ваши вопросы, я бы использовал версию 2.
источник
Верните список напрямую. Льготы:
Список можно использовать повторно. (итератор не) насамом деле не правда, спасибо ДжонВы должны использовать итератор (yield), когда вы думаете, что вам, вероятно, не придется выполнять итерацию до конца списка или когда у него нет конца. Например, клиентский вызов будет искать первый продукт, который удовлетворяет некоторому предикату, вы можете подумать об использовании итератора, хотя это надуманный пример, и, возможно, есть более эффективные способы его выполнения. В принципе, если вы заранее знаете, что весь список нужно будет рассчитать, просто сделайте это заранее. Если вы думаете, что это не так, то подумайте об использовании версии итератора.
источник
Ключевая фраза yield return используется для поддержки конечного автомата для конкретной коллекции. В тех случаях, когда CLR видит используемую ключевую фразу yield return, CLR реализует шаблон Enumerator для этого фрагмента кода. Этот тип реализации помогает разработчику из всех типов сантехники, которые в противном случае пришлось бы делать в отсутствие ключевого слова.
Предположим, что разработчик фильтрует некоторую коллекцию, выполняет итерацию по коллекции и затем извлекает эти объекты в какую-то новую коллекцию. Этот вид сантехники довольно однообразен.
Подробнее о ключевом слове здесь в этой статье .
источник
Использование yield аналогично ключевому слову return , за исключением того, что оно возвращает генератор . И объект генератора будет проходить только один раз .
У выхода есть два преимущества:
Есть еще одно четкое объяснение, может быть, вам поможет.
источник