В вопросе Как я могу раскрыть только фрагмент IList <> у одного из ответов был следующий фрагмент кода:
IEnumerable<object> FilteredList()
{
foreach(object item in FullList)
{
if(IsItemInPartialList(item))
yield return item;
}
}
Что здесь делает ключевое слово yield? Я видел ссылки в нескольких местах, и еще один вопрос, но я не совсем понял, что он на самом деле делает. Я привык думать о доходности в том смысле, что один поток уступает другому, но здесь это не актуально.
Ответы:
yield
Ключевые слова на самом деле делают довольно много здесь.Функция возвращает объект, который реализует
IEnumerable<object>
интерфейс. Если вызывающая функция начинает работатьforeach
над этим объектом, функция вызывается снова до тех пор, пока она не «выдаст». Это синтаксический сахар, введенный в C # 2.0 . В более ранних версиях вы должны были создавать свои собственные объектыIEnumerable
иIEnumerator
объекты для подобных вещей.Самый простой способ понять такой код - набрать пример, установить несколько точек останова и посмотреть, что произойдет. Попробуйте пройти этот пример:
Проходя по примеру, вы найдете первый вызов
Integers()
return1
. Второй вызов возвращается,2
и линияyield return 1
не выполняется снова.Вот реальный пример:
источник
yield break;
когда вы не хотите возвращать больше предметов.yield
это не ключевое слово Если бы это было так, я бы не смог использовать yield в качестве идентификатора, как вint yield = 500;
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'
, не звучит правильно для меня. Я всегда думал о ключевом слове c # yield в контексте «урожай дает обильный урожай», а не «автомобиль уступает пешеходу».Итерация. Он создает конечный автомат «под прикрытием», который запоминает, где вы были на каждом дополнительном цикле функции, и получает информацию оттуда.
источник
У выхода есть два отличных применения,
Это помогает обеспечить пользовательскую итерацию без создания временных коллекций.
Это помогает делать итерацию с учетом состояния.![введите описание изображения здесь](https://i.stack.imgur.com/Sza04.png)
Чтобы более наглядно объяснить вышеупомянутые два момента, я создал простое видео, которое вы можете посмотреть здесь.
источник
yield
. Статья проекта кода @ ShivprasadKoirala Какая польза от C # Yield? того же объяснения также является хорошим источникомyield
это «быстрый» способ создания пользовательского IEnumerator (вместо того, чтобы класс реализовывал интерфейс IEnumerator).Недавно Раймонд Чен также опубликовал интересную серию статей по ключевому слову yield.
Хотя он номинально используется для простой реализации шаблона итератора, но может быть обобщен в конечный автомат. Нет смысла цитировать Рэймонда, последняя часть также ссылается на другое использование (но пример в блоге Энтина особенно хорош, показывая, как писать асинхронный безопасный код).
источник
На первый взгляд возвращаемая доходность - это сахар .NET, возвращающий IEnumerable .
Без выхода все элементы коллекции создаются сразу:
Тот же код с помощью yield, он возвращает элемент за элементом:
Преимущество использования yield заключается в том, что если функция, потребляющая ваши данные, просто нуждается в первом элементе коллекции, остальные элементы не будут созданы.
Оператор yield позволяет создавать элементы по мере необходимости. Это хорошая причина, чтобы использовать его.
источник
yield return
используется с счетчиками. При каждом вызове оператора yield управление возвращается вызывающей стороне, но оно обеспечивает поддержание состояния вызываемой стороны. Вследствие этого, когда вызывающий объект перечисляет следующий элемент, он продолжает выполнение в методе вызываемого оператора сразу послеyield
оператора.Давайте попробуем понять это на примере. В этом примере, соответствующем каждой строке, я упомянул порядок, в котором выполняется выполнение.
Также состояние сохраняется для каждого перечисления. Предположим, у меня есть другой вызов
Fibs()
метода, тогда состояние будет сброшено для него.источник
Интуитивно понятно, что ключевое слово возвращает значение из функции, не покидая его, т.е. в вашем примере кода оно возвращает текущее
item
значение и затем возобновляет цикл. Более формально, он используется компилятором для генерации кода для итератора . Итераторы - это функции, которые возвращаютIEnumerable
объекты. В MSDN есть несколько статей о них.источник
Реализация списка или массива загружает все элементы немедленно, тогда как реализация yield обеспечивает решение отложенного выполнения.
На практике часто желательно выполнять минимальный объем работы по мере необходимости, чтобы уменьшить потребление ресурсов приложением.
Например, у нас может быть приложение, которое обрабатывает миллионы записей из базы данных. Следующие преимущества могут быть достигнуты при использовании IEnumerable в модели на основе отложенного выполнения:
Вот сравнение между созданием первой коллекции, такой как список, по сравнению с использованием yield.
Пример списка
Вывод на консоль
ContactListStore: создание контакта 1
ContactListStore: создание контакта 2
ContactListStore: создание контакта 3
Готов к просмотру коллекции.
Примечание: вся коллекция была загружена в память, даже не запрашивая ни одного элемента в списке
Пример доходности
Консольный вывод
Готов к итерации по коллекции.
Примечание: коллекция не была выполнена вообще. Это связано с природой IEnumerable «отложенного выполнения». Построение предмета будет происходить только тогда, когда это действительно необходимо.
Давайте снова вызовем коллекцию и изменим поведение при получении первого контакта в коллекции.
Вывод на консоль
Готов
перебратьколлекцию ContactYieldStore: создание контакта 1
Hello Bob
Ницца! Только первый контакт был создан, когда клиент «вытащил» элемент из коллекции.
источник
Вот простой способ понять концепцию: основная идея заключается в том, что если вам нужна коллекция, которую вы можете использовать "
foreach
", но сбор элементов в коллекцию по какой-то причине стоит дорого (например, запрос их из базы данных), И вам часто не понадобится вся коллекция, тогда вы создаете функцию, которая создает коллекцию по одному элементу за раз и возвращает ее потребителю (который затем может прекратить сбор данных раньше).Подумайте об этом так: вы идете к прилавку с мясом и хотите купить фунт нарезанной ветчины. Мясник берет 10-фунтовую ветчину в спину, кладет ее на слайсер, нарезает все на куски, затем возвращает вам кучу ломтиков и отмеряет фунт. (СТАРЫЙ путь). С
yield
помощью кнопки мясник подносит слайсер к прилавку и начинает нарезать и «подавать» каждый ломтик на весы до тех пор, пока он не замерет 1 фунт, а затем упаковывает его для вас, и все готово. Старый Путь может быть лучше для мясника (позволяет ему организовать свою технику так, как ему нравится), но Новый Путь явно более эффективен в большинстве случаев для потребителя.источник
yield
Ключевые слова позволяют создатьIEnumerable<T>
в формах на блоке итератора . Этот блок итератора поддерживает отложенное выполнение, и если вы не знакомы с концепцией, он может показаться почти волшебным. Однако, в конце концов, это просто код, который выполняется без каких-либо странных уловок.Блок итератора может быть описан как синтаксический сахар, где компилятор генерирует конечный автомат, который отслеживает, как далеко продвинулось перечисление перечисляемого. Чтобы перечислить перечислимое, вы часто используете
foreach
цикл. Однакоforeach
петля также является синтаксическим сахаром. Таким образом, вы удалили две абстракции из реального кода, поэтому изначально может быть трудно понять, как все это работает вместе.Предположим, что у вас есть очень простой блок итератора:
Реальные блоки итераторов часто имеют условия и циклы, но когда вы проверяете условия и разворачиваете циклы, они все равно оказываются
yield
чередованием операторов с другим кодом.Для перечисления блока итератора используется
foreach
цикл:Вот результат (здесь нет сюрпризов):
Как указано выше,
foreach
это синтаксический сахар:В попытке распутать это я создал диаграмму последовательности с удаленными абстракциями:
Конечный автомат, сгенерированный компилятором, также реализует перечислитель, но чтобы сделать диаграмму более понятной, я показал их как отдельные экземпляры. (Когда конечный автомат перечисляется из другого потока, вы фактически получаете отдельные экземпляры, но эта деталь здесь не важна.)
Каждый раз, когда вы вызываете свой блок итератора, создается новый экземпляр конечного автомата. Тем не менее, ни один из вашего кода в блоке итератора не выполняется, пока не будет выполнен
enumerator.MoveNext()
в первый раз. Вот как работает отложенное выполнение. Вот (довольно глупый) пример:На данный момент итератор не выполнен. Предложение
Where
создает новый,IEnumerable<T>
который оборачиваетIEnumerable<T>
возвращаемый объект ,IteratorBlock
но этот перечислимый еще предстоит перечислить. Это происходит, когда вы выполняетеforeach
цикл:Если вы перечислите перечислимое дважды дважды, каждый раз создается новый экземпляр конечного автомата, и ваш блок итератора будет выполнять один и тот же код дважды.
Обратите внимание , что методы LINQ нравится
ToList()
,ToArray()
,First()
, иCount()
т.д. будет использоватьforeach
цикл для перечисления перечислимых. НапримерToList()
будет перечислять все элементы перечисляемого и сохранять их в списке. Теперь вы можете получить доступ к списку, чтобы получить все элементы перечислимого без повторного выполнения блока итератора. Существует компромисс между использованием ЦП для создания элементов перечисляемого множества раз и памяти для хранения элементов перечисления для многократного доступа к ним при использовании подобных методовToList()
.источник
Если я правильно понимаю, вот как бы я это сформулировал с точки зрения функции, реализующей IEnumerable с yield.
источник
Проще говоря, ключевое слово yield C # позволяет много вызовов к телу кода, называемому итератором, который знает, как вернуться до того, как это будет сделано, и, при повторном вызове, продолжит работу с того места, на котором остановился - т.е. стать прозрачным с сохранением состояния для каждого элемента в последовательности, которую итератор возвращает при последовательных вызовах.
В JavaScript та же концепция называется Генераторы.
источник
Это очень простой и легкий способ создать перечисляемый для вашего объекта. Компилятор создает класс, который оборачивает ваш метод и реализует, в данном случае, IEnumerable <объект>. Без ключевого слова yield вам нужно создать объект, реализующий IEnumerable <объект>.
источник
Это производит перечисляемую последовательность. На самом деле он создает локальную последовательность IEnumerable и возвращает ее как результат метода
источник
Эта ссылка имеет простой пример
Еще более простые примеры здесь
Обратите внимание, что возвращаемая доходность не будет возвращаться из метода. Вы можете даже положить
WriteLine
послеyield return
Выше приведено IEnumerable 4 целых 4,4,4,4
Здесь с
WriteLine
. Добавьте 4 в список, напечатайте abc, затем добавьте 4 в список, затем завершите метод и, таким образом, действительно вернитесь из метода (как только метод завершится, как это будет происходить с процедурой без возврата). Но это будет иметь значение,IEnumerable
списокint
s, который он возвращает по завершении.Также обратите внимание, что когда вы используете yield, то, что вы возвращаете, не того же типа, что и функция. Это тип элемента в
IEnumerable
списке.Вы используете yield с типом возврата метода как
IEnumerable
. Если тип возвращаемого метода равенint
или,List<int>
и вы используетеyield
, то он не скомпилируется. Вы можете использоватьIEnumerable
метод возврата типа без yield, но, возможно, вы не можете использовать yield безIEnumerable
метода возврата метода.И чтобы заставить его исполниться, вы должны назвать это особым образом.
источник
public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }
иpublic static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
yield return
хорошо знаю (кроме простой вещи, которую я упомянул), и не использовал ее много, и не знаю много о ее использовании, я не думаю, что она должна быть принятой.Одним из основных моментов, связанных с ключевым словом Yield, является Lazy Execution . Теперь, что я подразумеваю под Lazy Execution, это выполнять при необходимости. Лучший способ выразить это - дать пример
Пример: не используется доходность, т.е. нет отложенного выполнения.
Пример: использование Yield т.е. Lazy Execution.
Теперь, когда я вызываю оба метода.
вы заметите, что внутри listItems будет 5 элементов (наведите курсор мыши на listItems во время отладки). Тогда как yieldItems будет просто иметь ссылку на метод, а не на элементы. Это означает, что он не выполнил процесс получения элементов внутри метода. Очень эффективный способ получения данных только при необходимости. Реальную реализацию yield можно увидеть в ORM, таких как Entity Framework, NHibernate и т. Д.
источник
Он пытается ввести некоторую
концепцию Ruby Goodness :) : это пример кода Ruby, который распечатывает каждый элемент массива
Массива каждая реализация методы дает контроль над вызывающим абонентом (в «пут х») с каждым элементом массива аккуратно представлены в виде х. Затем вызывающая сторона может делать с x все, что ей нужно.
Однако .Net здесь не доходит до конца. Кажется, что C # связал выход с IEnumerable, что заставило вас написать цикл вызова в вызывающей программе, как видно из ответа Менделя. Немного менее элегантно.
источник
yield
связан сIEnumerable
, а в C # отсутствует концепция Ruby «блока». Но в C # есть лямбды, которые могут позволить реализациюForEach
метода, очень похожего на Rubyeach
. Это, однако, не означает, что это будет хорошей идеей .