Различие между итератором и перечислителем - PullRequest
70 голосов
/ 04 апреля 2009

Вопрос интервью для работы .NET 3.5: «В чем разница между итератором и перечислителем»?

Это основное различие, которое нужно делать с LINQ и т. Д.

В любом случае, в чем разница? Я не могу найти четкое определение в сети. Не заблуждайтесь, я могу найти значение двух терминов, но получаю немного разные ответы. Какой будет лучший ответ для интервью?

IMO, итератор "перебирает" коллекцию, а перечислитель предоставляет функциональность для перебора, но это нужно вызывать.

Кроме того, используется ключевое слово yield для сохранения состояния. Что именно это состояние? Есть ли пример такого преимущества?

Ответы [ 8 ]

51 голосов
/ 04 апреля 2009

Итерация означает повторение некоторых шагов, а перечисление означает прохождение всех значений в коллекции значений. Поэтому перечисление обычно требует некоторой формы итерации.

Таким образом, перечисление является частным случаем итерации, когда шаг получает значение из коллекции.

Обратите внимание, что "обычно" - перечисление также может выполняться рекурсивно, но рекурсия и итерация настолько тесно связаны, что мне было бы наплевать на эту небольшую разницу.

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


Полагаю, Рид Копси понял. В C # есть два основных способа перечисления чего-либо.

  1. Реализация Enumerable и реализация класса IEnumerator
  2. Реализация итератора с оператором yield

Первый способ сложнее реализовать и использует объекты для перечисления. Второй способ проще в реализации и использует продолжения.

43 голосов
/ 04 апреля 2009

В C # 2+ итераторы позволяют компилятору автоматически генерировать интерфейсы IEnumerable и / или IEnumerable для вас.

Без итераторов вам потребуется создать класс, реализующий IEnumerator , включая Current, MoveNext и Reset. Это требует изрядного количества работы. Обычно вы создаете закрытый класс, который реализует IEnumerator для вашего типа, затем yourClass.GetEnumerator () создаст этот закрытый класс и вернет его.

Итераторы позволяют компилятору автоматически генерировать это для вас, используя простой синтаксис (yield). Это позволяет вам реализовать GetEnumerator () непосредственно в вашем классе, без указания второго класса (IEnumerator). Построение этого класса со всеми его членами сделано для вас.

Итераторы очень удобны для разработчиков - все сделано очень эффективно, с гораздо меньшими усилиями.

Когда вы используете foreach, они будут вести себя одинаково (при условии, что вы правильно написали свой пользовательский IEnumerator). Итераторы просто упрощают жизнь.

18 голосов
/ 27 октября 2009

То, что C # называет итератором , чаще (вне мира C #) называется генератором или функцией генератора (например, в Python). Функция генератора является специализированным случаем сопрограмм . Итератор C # (генератор) - это специальная форма перечислителя (тип данных, реализующий интерфейс IEnumerable).

Мне не нравится это использование термина итератор для генератора C #, потому что он так же, как и итератор. Однако Microsoft слишком поздно передумает.

Для сравнения рассмотрим, что в C ++ итератор - это значение, которое используется главным образом для доступа к последовательным элементам в коллекции. Его можно продвигать, вызывать для получения значения и проверять, достигнут ли конец коллекции.

12 голосов
/ 04 апреля 2009

Чтобы понять итераторы, нам сначала нужно понять перечислители.

Перечислители - это специализированные объекты, которые предоставляют один из средств для перемещения по упорядоченному списку элементов по одному (такие же вещи иногда называют «курсором»). .NET Framework предоставляет два важных интерфейса, относящихся к перечислителям: IEnumerator и IEnumerable. Объекты, которые реализуют IEnumerator, сами являются перечислителями; они поддерживают следующих членов:

  • Свойство Current, которое указывает на позицию в списке

  • метод MoveNext, который перемещает Текущий элемент один по списку

  • метод Reset, который перемещает Текущий элемент в его начальную позицию (которая находится перед первым элементом).

С другой стороны, Итераторы реализуют шаблон перечислителя. В .NET 2.0 появился итератор, который представляет собой перечисляемый компилятором перечислитель. Когда перечислимый объект вызывает GetEnumerаtor, прямо или косвенно, компилятор генерирует и возвращает соответствующий объект итератора. Необязательно, итератор может быть комбинированным перечислимым и перечислимым объектом.

Основным компонентом итераторного блока является декларация о доходности. Существует одно большое различие между итераторами и перечислителями: итераторы не реализуют метод Reset. Вызов метода Reset в итераторе вызывает исключение.

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

11 голосов
/ 23 июля 2014

"Если оператор foreach является потребителем перечислителя, то итератор является производителем перечислителя."

Вышесказанное объясняет, как «C # 5.0 In A NutShell» объясняет это, и было полезно для меня.

Другими словами, оператор foreach использует MoveNext () и свойство Current IEnumerator для итерации последовательности, тогда как итератор используется для создания реализации IEnumerator, которая будет использоваться оператором foreach. В C #, когда вы пишете метод итератора, содержащий оператор yield, компилятор сгенерирует для вас закрытый перечислитель. И когда вы перебираете элементы в последовательности, он вызывает свойства MoveNext () и Current частного перечислителя. Эти методы / свойства реализуются вашим кодом в методе итератора, который будет вызываться повторно для получения значений до тех пор, пока не останется значений для выхода.

Это мое понимание того, как C # определяет перечислители и итераторы.

7 голосов
/ 02 февраля 2011

Поскольку не было приведено ни одного примера, вот тот, который был мне полезен.

Перечислитель - это объект, который вы получаете, когда вызываете .GetEnumerator () для класса или типа, который реализует интерфейс IEnumerator. Когда этот интерфейс реализован, вы создали весь код, необходимый для компилятора, чтобы вы могли использовать foreach для «перебора» вашей коллекции.

Не путайте это слово «итерация» с итератором. И перечислитель, и итератор позволяют «итерировать». Перечисление и повторение - это в основном один и тот же процесс, но они реализованы по-другому. интерфейс IEnumerator. Итерация означает, что вы создали конструкцию итератора в своем классе (показано ниже), и вы вызываете foreach для своего класса, после чего компилятор автоматически создает функциональность перечислителя для вас.

Также обратите внимание, что вам не нужно приседать со своим счетчиком. Вы можете звонить MyClass.GetEnumerator() весь день и ничего с этим не делать (пример:

IEnumerator myEnumeratorThatIWillDoNothingWith = MyClass.GetEnumerator()).

Также обратите внимание, что ваша конструкция итератора в вашем классе действительно используется только тогда, когда вы на самом деле ее используете, то есть вы вызывали foreach в своем классе.

Вот пример итератора из msdn :

public class DaysOfTheWeek : System.Collections.IEnumerable
{

     string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

     //This is the iterator!!!
     public System.Collections.IEnumerator GetEnumerator()
     {
         for (int i = 0; i < days.Length; i++)
         {
             yield return days[i];
         }
     }

}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach - this is using the iterator!!! When the compiler
        //detects your iterator, it will automatically generate the Current, 
        //MoveNext and Dispose methods of the IEnumerator or IEnumerator<T> interface
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}
// Output: Sun Mon Tue Wed Thr Fri Sat
3 голосов
/ 04 апреля 2009

"Итераторы - это новая функция в C # 2.0. Итератор - это метод, метод доступа get или оператор, который позволяет поддерживать итерацию foreach в классе или структуре без необходимости реализации всего интерфейса IEnumerable. Вместо этого вы предоставляете только итератор, который просто просматривает структуры данных в вашем классе. Когда компилятор обнаружит ваш итератор, он автоматически сгенерирует методы Current, MoveNext и Dispose интерфейса IEnumerable или IEnumerable. " - MSDN

2 голосов
/ 04 апреля 2009

Перечисление работает с объектами, а итерация - только со значениями. Перечисление используется, когда мы используем векторную хеш-таблицу и т. Д., В то время как итерация используется в цикле while для цикла и т. Д. Я никогда не использовал ключевое слово yield, поэтому я не могу вам этого сказать.

...