C#: IEnumerable, GetEnumerator, a simple, simple example please!
Trying to create an uebersimple class that implements get enumerator, but failing madly due to lack of simple / non-functioning examples out there. All I want to do is create a wrapper around a data structure (in this case a list, but I might need a dictionary later) and add some functions.
4 Answers 4
You can simply return the enumerator returned by List<T>.GetEnumerator:
In addition to the other answers if you need a little more control over how the enumerator works or if there is a requirement to customize it beyond what the underlying data structure can provide then you can use the yield keyword.
![]()
or the simplified version:
I wouldn’t advice leaving out the IEnumerable<T> interface, because you loose integration with .NET such as possibilities to use LINQ, but you can iterate over the collection using a foreach in C#.
Or this one is even shorter 🙂
Of course this last one is a mutable list, which perhaps is not exactly what you want.
Getenumerator c как реализовать
Как мы увидели, основной для большинства коллекций является реализация интерфейсов IEnumerable и IEnumerator. Благодаря такой реализации мы можем перебирать объекты в цикле foreach :
Перебираемая коллекция должна реализовать интерфейс IEnumerable.
Интерфейс IEnumerable имеет метод, возвращающий ссылку на другой интерфейс — перечислитель:
А интерфейс IEnumerator определяет функционал для перебора внутренних объектов в контейнере:
Метод MoveNext() перемещает указатель на текущий элемент на следующую позицию в последовательности. Если последовательность еще не закончилась, то возвращает true. Если же последовательность закончилась, то возвращается false.
Свойство Current возвращает объект в последовательности, на который указывает указатель.
Метод Reset() сбрасывает указатель позиции в начальное положение.
Каким именно образом будет осуществляться перемещение указателя и получение элементов зависит от реализации интерфейса. В различных реализациях логика может быть построена различным образом.
Например, без использования цикла foreach перебирем массив с помощью интерфейса IEnumerator:
Реализация IEnumerable и IEnumerator
Рассмотрим простешую реализацию IEnumerable на примере:
В данном случае класс Week, который представляет неделю и хранит все дни недели, реализует интерфейс IEnumerable. Однако в данном случае мы поступили очень просто — вместо реализации IEnumerator мы просто возвращаем в методе GetEnumerator объект IEnumerator для массива.
Благодаря этому мы можем перебрать все дни недели в цикле foreach.
В то же время стоит отметить, что для перебора коллекции через foreach в принципе необязательно реализовать интерфейс IEnumerable. Достаточно в классе определить публичный метод GetEnumerator , который бы возвращал объект IEnumerator. Например:
Однако это было довольно просто — мы просто используем уже готовый перчислитель массива. Однако, возможно, потребуется задать свою собственную логику перебора объектов. Для этого реализуем интерфейс IEnumerator :
Здесь теперь класс Week использует не встроенный перечислитель, а WeekEnumerator, который реализует IEnumerator.
Ключевой момент при реализации перечислителя — перемещения указателя на элемент. В классе WeekEnumerator для хранения текущей позиции определена переменная position. Следует учитывать, что в самом начале (в исходном состоянии) указатель должен указывать на позицию условно перед первым элементом. Когда будет производиться цикл foreach, то данный цикл вначале вызывает метод MoveNext и фактически перемещает указатель на одну позицию в перед и только затем обращается к свойству Current для получения элемента в текущей позиции.
Затем в программе мы можем аналогичным образом перебирать объект Week с помощью цикла foreach:
Обобщенная версия IEnumerator
В примерах выше использовались необобщенные версии интерфейсов, однако мы также можем использовать их обобщенные двойники:
В данном случае реализуем интерфейс IEnumerator<string> , соответственно в свойстве Current нам надо возвратить объект string. В этом случае при переборе в цикле foreach перебираемые объекты будут автоматически представлять тип string :
Коллекции объектов. Интерфейсы IEnumerable и IEnumerator
В подавляющем большинстве случаев, возможности платформы .NET покрывают наши потребности в тех или иных классах для хранения и перебора элементов коллекций. Так, в пространствах имен System.Collections и System.Collections.Generics содержатся различные уже готовые классы для работы с коллекциями — List<T> , Stack<T> и другие, с помощью которых можно реализовать необходимую функциональность. Однако, иногда все же может потребоваться реализовать в программе собственный класс, содержащий массив элементов и способный эти элементы перечислять. В этом случае нам потребуется реализовать в своих классах интерфейсы IEnumerable и IEnumerator .
Возвращаясь к нашему примеру с классом Box : в этом классе мы уже реализовали интерфейс IClonable для клонирования объекта и интерфейс IComparable для сравнения двух объектов типа Box . Кром этого, мы создали свой класс-компаратор, с помощью которого научились сортировать наши коробки по разным параметрам. Продолжим работу с этим классом и попробуем создать класс, который будет перечислять все наши объекты типа Box .
Интерфейс IEnumarable
Интерфейс IEnumerable предоставляет перечислитель, который поддерживает простой перебор элементов неуниверсальной коллекции. Другими словами, класс, который реализует этот интерфейс, можно использовать в цикле foreach для перебора всех элементов его коллекции. Создадим новый класс, реализующий интерфейс IEnumarable для перечисления всех объектов Box , находящихся в коллекции.
В конструкторе BoxStorage принимает массив объектов Box . Все ссылки на элементы массива _boxes копируются во внутренний массив boxes . Интерфейс IEnumarable содержит всего один метод — GetEnumerator , который должен возвратить объект, реализующий интерфейс IEnumerator (перечислитель).
Интерфейс IEnumerator
Реализуем интерфейс следующим образом:
Интерфейс IEnumerator содержит:
- свойство Current , которое должно возвращать нам очередной элемент коллекции;
- метод MoveNext , который возвращает True , в случае, если можно обратиться к очередному элементу коллекции;
- метод Reset , который сбрасывает счётчик элементов на значение по умолчанию ( -1 )
Для реализации интерфейса мы использовали в классе две переменные : currIndex — индекс очередного элемента массива и enumBoxes — собственно, сам массив элементы которого необходимо перечислить. Теперь нам необходимо дописать метод GetEnumerator в классе BoxStorage :
Теперь наш класс может использоваться в цикле foreach для перебора всех элементов массива, не раскрывая при этом сам массив в публичный доступ:
Итого
Используя интерфейсы IEnumerable и IEnumerator мы можем создавать собственные классы, позволяющие перечислять коллекции элементов определенного типа. В большинстве случаев нам достаточно тех классов, которые уже содержатся в .NET, однако, если вам необходимо реализовать собственный тип коллекции элементов, которая должна поддерживать перечисление с использованием цикла foreach, то в этом случае вам необходимо реализовать в своем проекте два интерфейса — IEnumerable и IEnumerator .
Перечисления и коллекции в C#
Платформа .NET содержит набор типов для хранения и управления коллекциями объектов: списки с изменяемыми размерами, связанные списки, отсортированные и неотсортированные словари, массивы.
Типы в .NET для коллекций можно разделить на три категории:
- интерфейсы, определяющие стандартные протоколы коллекций
- готовые к использованию классы коллекций (списки, словари)
- базовые классы для написания коллекций
Все эти типы расположены в следующих пространствах имен:
- System.Collections — необобщенные классы и интерфейсы коллкций
- System.Collections.Specialized — строго типизированные необобщенные классы коллекций
- System.Collections.Generic — обобщенные классы и интерфейсы коллекций
- System.Collections.ObjectModel — прокси и базовые классы для специальных коллекций
- System.Collections.Concurrent — коллекции, безопасные к потокам
Все коллекции являются перечислениями и реализуют (должны реализовывать) интерфейс IEnumerable или IEnumerable<T> .
Интерфейсы перечислений обеспечивают протокол для итерации по коллекции, но не предоставляют механизма для определения размера коллекции, доступа к члену по индексу, поиска или модификации коллекции. Для данного функционала определены интерфейсы ICollection , IList и IDictionary . Каждый из них имеет обобщенную и необобщенную версию (однако необобщенные используются редко).
Иерархия интерфейсов коллекций:
- IEnumerable<T> и IEnumerable — минимальный уровень функциональности: только перечисление
- ICollection<T> и ICollection — средний уровень функциональности, например, свойство Count
- IList <T> , IDictionary <K,V> и их необобщенные аналоги — максимальный уровень функциональности
Перечисление (Enumeration)
Нумератор, или перечислитель (enumerator) — доступный только для чтения однонаправленный курсор (forward-only cursor — курсор, движущийся только вперёд, без возможности обратного перемещения) перебирающий последовательность (sequence) значений. Нумератор представляет собой объект, реализующий интерфейс System.Collections.IEnumerator или System.Collections.Generic.IEnumerator<T> .
Инструкция foreach перебирает, или выполняет итерацию (iterate) над перечислимыми (enumerable) объектами. Перечислимые объекты — это логическое представление последовательностей. Перечислимый объект — это не курсор, о котором говорилось выше, а объект, содержащий такой курсор, способный перемещаться по объекту и перебирать его. Перечислимый объект либо реализует интерфейс IEnumerable или IEnumer able<T> , либо содержит метод GetEnumerator , который возвращает нумератор (enumerator).
Интерфейсы IEnumerable и IEnumerator
Интерфейс IEnumerator определяет базовый низкоуровневый протокол, посредством которого производится проход по элементам (перечисление) последовательности в одонаправленной манере. Объявление этого интерфейса: