Что значит «перерыв в доходах»; делать в C #?

494

Я видел этот синтаксис в MSDN:, yield breakно я не знаю, что он делает. Кто-нибудь знает?

SKB
источник
26
Тем не менее, документация MSDN упоминает об этом, но не объясняет это. Это плохой способ дразнить голодных до знаний разработчиков.
Фил
3
Возврат доходности устраняет необходимость в вспомогательном списке, то есть вам не нужно кодировать что-то вроде MyList.Add(...)просто делайте yield return .... Если вам нужно преждевременно выйти из цикла и вернуть используемый вами виртуальный резервный списокyield break;
The Muffin Man

Ответы:

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
В breakэтом случае простой @orad остановит цикл, но не прервет выполнение метода, поэтому будет выполнена последняя строка и на самом деле будет виден текст «Не вижу меня».
Дамир Зекич
5
@Damir Zekić Не могли бы вы также добавить к своему ответу, почему вы предпочитаете разрыв доходности, а не разницу между ними?
Бруно Коста
11
@ DamirZekić, возвращающий ноль и разрыв доходности, не то же самое. Если вы вернете ноль, то вы можете иметьNullReferenceException получить перечислитель IEnumerable, тогда как с yield break вы этого не сделаете (есть экземпляр без элементов).
Бруно Коста
6
@BrunoCosta Попытка регулярного возврата в одном и том же методе приводит к ошибке компилятора. Используя yield return xоповещения компилятора, вы хотите, чтобы этот метод был синтаксическим сахаром для создания Enumeratorобъекта. Этот перечислитель имеет метод MoveNext()и свойство Current. MoveNext () выполняет метод до yield returnоператора и преобразует это значение в Current. При следующем вызове MoveNext выполнение будет продолжено. yield breakустанавливает Current в null, сигнализируя об окончании этого перечислителя, так что a foreach (var x in myEnum())будет заканчиваться.
Wolfzoon
57

Завершает блок итератора (например, говорит, что в IEnumerable больше нет элементов).

Брайан
источник
2
с [+1] - хотя академически в .NET нет итераторов. только счетчики (в одном направлении, вперед.)
Шон Уилсон
@Shaun Wilson, но с помощью yieldключевого слова вы можете выполнять итерацию коллекции в обоих направлениях, более того, вы можете брать каждый следующий элемент коллекции не подряд
monstr
@monstr вы можете перебирать коллекцию любым способом, и вам не нужно yieldэто делать. я не говорю, что вы не правы, я понимаю, что вы предлагаете; но академически в .NET нет итераторов, только перечислители (в одном направлении, вперед) - в отличие от stdc ++, в CTS / CLR не определена «универсальная структура итераторов». LINQ помогает сократить разрыв с помощью методов расширения, которые используют, yield return а также методы обратного вызова , но они являются методами расширения первого класса, а не итераторами первого класса. результирующее IEnumerableсамо по себе не может повторяться в любом направлении, кроме пересылки по отношению к вызывающему.
Шон Уилсон,
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это как поток, вызывающий функцию «schedule» или «sleep» для отказа от управления процессором. Точно так же, как поток, IEnumerable<T>метод восстанавливает элементы управления в точке сразу же после того, как все локальные переменные имеют те же значения, что и до того, как управление было прекращено.

yield break это как поток, достигающий конца своей функции и заканчивающийся.

Люди говорят о «государственном автомате», но конечный автомат - это действительно «нить». Поток имеет некоторое состояние (т. Е. Значения локальных переменных), и каждый раз, когда он запланирован, он предпринимает некоторые действия для достижения нового состояния. Ключевым моментом здесь yieldявляется то, что, в отличие от потоков операционной системы, к которым мы привыкли, код, который его использует, заморожен во времени, пока итерация не будет расширена или завершена вручную.


источник
13

Вся тема блоков итераторов хорошо освещена в этой бесплатной главе из книги Джона Скита C # in Depth .

Марк Гравелл
источник
Вы только что продали мне копию. Спасибо! :-)
Джон Шнайдер
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/ это очень хороший пример:

public static IEnumerable <int> Range (int min, int max)
{
   пока (правда)
   {
      если (мин> = макс)
      {
         разрыв доходности;
      }
      доходность возврата мин ++;
   }
}

и объяснение, что если yield breakв метод попадает оператор, выполнение этого метода прекращается без возврата. Существуют временные ситуации, когда вы не хотите давать какой-либо результат, тогда вы можете использовать предел доходности.

Towhid
источник
5

yield break - это просто способ сказать return в последний раз и не возвращать никакого значения

например

// 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
Ну, они сложны, только если вы заботитесь о коде, в который они компилируются. Код, который их использует, довольно прост.
Роберт Росни
@ Роберт, вот что я имел в виду, поэтому я обновил ответ, включив твой комментарий
Тони Ли
-2

Ключевое слово yield используется вместе с ключевым словом return для предоставления значения объекту перечислителя. yield 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.

Важно отметить, что переменная counter в этом примере является локальной переменной. После второй итерации, которая возвращает значение 2, третья итерация начинается с того места, где она была ранее, сохраняя предыдущее значение локальной переменной с именем counter, равное 2.

Эранга Диссанаяка
источник
11
Вы не объяснили, что yield breakделает
Джей Салливан
Я не думаю, что на yield returnсамом деле поддерживает возвращение нескольких значений. Может быть, это не то, что вы на самом деле имели в виду, но так я это прочитал.
Сэм
Sam - метод SampleNumbers с несколькими операторами yield return на самом деле работает, значение итератора возвращается немедленно и выполнение возобновляется при запросе следующего значения. Я видел, как люди заканчивают такой метод "разрывом доходности", но это не нужно. Попадание в конец метода также завершает итератор
Лорен Полсен
причина того, что это плохой пример, yield breakзаключается в том, что он не содержит перечислителя уровня языка, такого как foreach- при использовании перечислителя yield breakобеспечивает реальное значение. этот пример выглядит как развернутый цикл. вы почти никогда не увидите этот код в реальном мире (мы все можем подумать о некоторых крайних случаях, конечно же), здесь нет итератора. «блок итератора» не может выходить за пределы метода в зависимости от спецификации языка. то, что фактически возвращается, является «перечислимым», см. также: stackoverflow.com/questions/742497/…
Шон Уилсон,