Что делает «yield break» в C #?

367

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

  • 24
    Тем не менее, документация MSDN упоминает об этом, но не объясняет это. Это плохой способ дразнить голодных до знаний разработчиков.
  • 2
    Возврат доходности устраняет необходимость в вспомогательном списке, то есть вам не нужно кодировать что-то вроде MyList.Add(...) просто yield return ... Если вам нужно преждевременно выйти из цикла и вернуть виртуальный список поддержки, вы используете yield break;
Показать ещё 1 комментарий
Теги:
yield

9 ответов

404
Лучший ответ

Указывает, что итератор подошел к концу. Вы можете придумать 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");

В этом случае последнее утверждение никогда не выполняется, потому что мы рано оставили функцию.

  • 5
    Может ли это быть просто break вместо yield break в вашем примере выше? Компилятор не жалуется на это.
  • 65
    При простом break @orad в этом случае цикл будет остановлен, но он не прервет выполнение метода, поэтому будет выполнена последняя строка и на самом деле будет виден текст «Не вижу меня».
Показать ещё 10 комментариев
41

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

  • 2
    с [+1] - хотя академически в .NET нет итераторов. только счетчики (одно направление, вперед.)
  • 0
    @Shaun Wilson, но с помощью ключевого слова yield вы можете выполнять итерацию коллекции в обоих направлениях, более того, вы можете брать каждый следующий элемент коллекции не подряд
Показать ещё 1 комментарий
28

Сообщает итератору, что он достиг цели.

В качестве примера:

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;
    }
}
18

yield в основном заставляет метод IEnumerable<T> вести себя аналогично совместному (в отличие от упреждающего) планового потока.

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

yield break похож на поток, достигающий конца его функции и завершающий.

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

10

Весь предмет блоков итераторов хорошо освещен в этой свободной главе примера из книги Джона Скита С# в глубине.

  • 0
    Вы только что продали мне копию. Спасибо! :-)
6

Здесь http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ - очень хороший пример:

public static IEnumerable<int> Range( int min, int max )
{
   while ( true )
   {
      if ( min >= max )
      {
         yield break;
      }
      yield return min++;
   }
}

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

4

Оператор 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(..).

4

Если то, что вы подразумеваете под словом "то, что действительно дает перерыв", - "как это работает" - см. блог Raymond Chen для более подробной информации http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

Итераторы С# генерируют очень сложный код.

  • 11
    Ну, они сложны, только если вы заботитесь о коде, в который они скомпилированы. Код, который их использует, довольно прост.
  • 0
    @ Роберт, это я и имел ввиду, поэтому я обновил ответ, включив твой комментарий
0

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

  • 10
    Вы не объяснили, что делает yield break
  • 0
    Я не думаю, что yield return фактически поддерживает возврат нескольких значений. Может быть, это не то, что вы на самом деле имели в виду, но так я это прочитал.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню