2009-04-04 4 views
62

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

Это ядро ​​различие сделать, что с LINQ и т.д.

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

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

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

ответ

42

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

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

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

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


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

  1. Реализовать Enumerable и класс реализации IEnumerator
  2. Реализация итератора с yield заявление

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

+5

В C#, однако, итератор представляет собой специфическую специальную конструкцию, а также только глагол, описывающий этот термин. Перечислитель также является конкретным интерфейсом. Эти два имеют совершенно другое значение в C#, а также обычные члены OO. –

+2

О да, вы правы. В моих терминах это все перечисляется, и я полностью забыл, что C# вызывает этот итератор шаблона. –

2

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

+0

Это может быть правдой, но только для сети точек. На некоторых языках вы можете повторять объекты/значения и перечислять объекты/значения. –

+0

Ну, я программист .NET. Извините за мое невежество, именно так я узнал, как они работают. – Kredns

3

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

41

В C# 2+, iterators есть способ для компилятора автоматически генерировать IEnumerable и/или IEnumerable < T интерфейсы для вас.

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

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

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

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

12

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

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

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

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

  • метод Сброс, который перемещает текущий элемент в исходное положение (которое находится перед первым элементом).

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

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

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

16

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

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

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

+2

Ну, это лучший ответ здесь;) – mrzepa

7

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

Перечислитель - это объект, который вы получаете при вызове .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 
9

«В то время как Еогеасп утверждение является потребителем счетчику, итератор является производителем счетчику.»

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

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

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

+0

Это предложение суммирует его очень лаконично. Жаль, что этот ответ так далеко, пусть мой +1! – AnorZaken