Що означає "врожайність"; робити в C #?


494

Я бачив цей синтаксис у MSDN:, yield breakале я не знаю, що це робить. Хтось знає?


26
Тим не менш, документація MSDN згадує це, але не пояснює. Це поганий спосіб дражнити голодного розробника.
Філ

3
Повернення виходу усуває необхідність створення списку резервних копій, тобто вам не потрібно кодувати щось на кшталт MyList.Add(...)просто робити yield return .... Якщо вам потрібно передчасно вийти з циклу і повернути використаний вами список віртуальних резервних yield break;
копій

Відповіді:


529

Він вказує, що ітератор закінчився. Ви можете вважати yield breakце returnтвердженням, яке не повертає значення.

Наприклад, якщо ви визначаєте функцію як ітератор, тіло функції може виглядати так:

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

Зауважте, що після завершення циклу всі його цикли виконується останній рядок, і ви побачите повідомлення у своєму консольному додатку.

Або так yield break:

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

У цьому випадку останнє твердження ніколи не виконується, тому що ми покинули функцію рано.


8
Чи може це бути просто breakзамість yield breakвашого прикладу вище? Компілятор на це не скаржиться.
орад

85
@orad простий breakу цьому випадку зупинить цикл, але він не скасує виконання методу, таким чином, останній рядок буде виконаний, а текст "Не побачить мене" насправді буде видно.
Дамір Зекіч

5
@Damir Zekić. Чи можете ви також додати свою відповідь, чому вам слід віддати перевагу прибутковості над прибутковістю та які відмінності між ними?
Бруно Коста

11
@ ДамірЗекич, що повертається з нульовим показником, і перерва врожайності не однакова. Якщо ви повернете null, то у вас може бути NullReferenceExceptionперерахунок IEnumerable, тоді як з перервою урожайності ви цього не зробите (є екземпляр без елементів).
Бруно Коста

6
@BrunoCosta Намагання регулярного повернення в тому ж методі дає помилку компілятора. Використовуючи yield return xпопередження компілятора, ви хочете, щоб цей метод був синтаксичним цукром для створення Enumeratorоб'єкта. Цей перелік має метод MoveNext()та властивість Current. MoveNext () виконує метод до yield returnоператора і перетворює це значення в Current. Наступного разу, коли MoveNext буде викликано, звідти триває виконання. yield breakвстановлює значення Current на нуль, сигналізуючи про кінець цього перелічувача, так що a foreach (var x in myEnum())закінчиться.
Вольфзун

57

Закінчується блоком ітератора (наприклад, говорить, що в IEnumerable більше елементів немає).


2
з [+1] - хоча в .NET академічно немає ітераторів. тільки перелічувачі (в одному напрямку, вперед.)
Шон Вілсон

@Shaun Wilson, але за допомогою yieldключового слова ви можете повторити колекцію в обох напрямках, більше того, ви можете взяти кожен наступний елемент колекції не поспіль
понеділок

@monstr ви можете ітератувати колекцію будь-яким способом і вам цього не потрібно yieldробити. я не кажу, що ти не прав, я бачу, що ти пропонуєш; але, в академічному відношенні в .NET немає ітераторів, лише нумератори (один напрямок, вперед) - на відміну від stdc ++ немає "загальної рамки ітератора", визначеної в CTS / CLR. LINQ допомагає усунути розрив методами розширення, які використовують, yield return а також методи зворотного виклику , але вони є першокласними методами розширення, а не першокласними ітераторами. отриманий результат IEnumerableне може сам ітератувати в іншому напрямку, окрім як перемотати wrt абонента.
Шон Вілсон

29

Повідомляє ітератору, що він досяг кінця.

Як приклад:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

21

yieldв основному змушує IEnumerable<T>метод вести себе аналогічно кооперативному (на відміну від превентивного) запланованого потоку.

yield returnце як потік, що викликає функцію "розклад" або "сон", щоб відмовитися від управління процесором. Подібно до потоку, IEnumerable<T>метод знову отримує елементи керування в точці, після чого всі локальні змінні мають ті ж значення, що й до того, як було передано контроль.

yield break це як нитка, що доходить до кінця своєї функції і закінчується.

Люди говорять про "державну машину", але державна машина - це все "нитка" насправді. Потік має деякий стан (тобто значення локальних змінних), і кожен раз, коли це заплановано, він виконує певні дії, щоб досягти нового стану. Ключовим моментом yieldє те, що на відміну від потоків операційної системи, до яких ми звикли, код, який використовує, заморожується в часі, поки ітерація не буде вручну розширена або припинена.



9

yield breakЗатвердження викликає перерахування для зупинки. Фактично, yield breakзавершує перерахування, не повертаючи жодних додаткових елементів.

Подумайте, що насправді є два способи, завдяки яким метод ітератора міг зупинити ітерацію. В одному випадку логіка методу може природно вийти з методу після повернення всіх елементів. Ось приклад:

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

У наведеному вище прикладі метод ітератора, природно, припинить виконання, коли maxCountзнайдені прайми.

yield breakЗаява є ще одним способом для ітератора припинити перерахування. Це спосіб вийти з перерахунку рано. Ось такий же спосіб, як і вище. Цього разу метод має обмеження кількості часу, який може виконати метод.

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

Помітьте заклик до yield break. Насправді, вона припиняє перерахунок достроково.

Зауважте також, що yield breakпрацює інакше, ніж просто звичайне break. У наведеному вище прикладі yield breakвиходить із методу, не здійснюючи виклик Debug.WriteLine(..).


7

Ось http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ - дуже хороший приклад:

загальнодоступний статичний IEnumerable <int> діапазон (int min, int max)
{
   поки (правда)
   {
      якщо (хв> = макс)
      {
         перерва врожайності;
      }
      віддача прибутковості min ++;
   }
}

та пояснення, що якщо yield breakоператор потрапляє в метод, виконання цього методу припиняється без повернення. Існують певні часові ситуації, коли ви не хочете давати жодного результату, тоді ви можете використовувати перерву врожайності.


5

перерва в урожайності - це лише спосіб сказати повернення в останній раз і не повертати жодного значення

напр

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

4

Якщо ви маєте на увазі під собою "що насправді робить врожайність", це "як це працює"? - Докладніше див. У блозі Раймонда Чена https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

C # ітератори генерують дуже складний код.


12
Ну, вони складні лише в тому випадку, якщо ви дбаєте про код, який вони складають. Код, який їх використовує, досить простий.
Роберт Россні

@Robert, це я мав на увазі, тому я оновив відповідь, щоб включити ваш коментар
Тоні Лі,

-2

Ключове слово прибутковість використовується разом із ключовим словом return для надання значення об’єкту перелічувача. прибутковість вказує значення, або значення, що повертаються. Коли дохід про повернення доходу досягнуто, поточне місце зберігається. Виконання буде перезапущено з цього місця під час наступного виклику ітератора.

Щоб пояснити значення за допомогою прикладу:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

Значення, повернені при повторенні, становлять: 0, 2, 5.

Важливо зазначити, що лічильна змінна в цьому прикладі є локальною змінною. Після другої ітерації, яка повертає значення 2, третя ітерація починається з того місця, де вона залишилась раніше, зберігаючи попереднє значення локальної змінної, названої лічильником, яке було 2.


11
Ви не пояснили, що yield breakробить
Джей Салліван

Я не думаю, що yield returnнасправді підтримує повернення кількох значень. Можливо, це не те, що ви насправді мали на увазі, але саме так я це прочитав.
Сем

Sam - метод SampleNumbers з декількома операторами повернення прибутків насправді спрацьовує, значення ітератора повертається негайно і виконання відновлюється при запиті наступного значення. Я бачив, як люди закінчують такий метод, як "перерва врожайності", але це зайве. Попадання кінця методу також закінчує ітератор
Лорен Полсен

Причина, для якої це поганий приклад, yield breakполягає в тому, що він не містить перелік рівня мови, наприклад, а foreach- при використанні перелічувача yield breakнадає реальне значення. цей приклад виглядає як розкручений цикл. ви майже ніколи не побачите цього коду в реальному світі (ми всі можемо придумати деякі крайові випадки), також немає "ітератора". "блок ітераторів" не може виходити за межі методу в залежності від мовної специфікації. що насправді повертається - це "перелік", див. також: stackoverflow.com/questions/742497/…
Shaun Wilson
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.