В подавляющем большинстве случаев, возможности платформы .NET покрывают наши потребности в тех или иных классах для хранения и перебора элементов коллекций. Так, в пространствах имен System.Collections
и System.Collections.Generics
содержатся различные уже готовые классы для работы с коллекциями — List<T>
, Stack<T>
и другие, с помощью которых можно реализовать необходимую функциональность. Однако, иногда все же может потребоваться реализовать в программе собственный класс, содержащий массив элементов и способный эти элементы перечислять. В этом случае нам потребуется реализовать в своих классах интерфейсы IEnumerable
и IEnumerator
.
Возвращаясь к нашему примеру с классом Box
: в этом классе мы уже реализовали интерфейс IClonable
для клонирования объекта и интерфейс IComparable
для сравнения двух объектов типа Box
. Кром этого, мы создали свой класс-компаратор, с помощью которого научились сортировать наши коробки по разным параметрам. Продолжим работу с этим классом и попробуем создать класс, который будет перечислять все наши объекты типа Box
.
Интерфейс IEnumarable
Интерфейс IEnumerable
предоставляет перечислитель, который поддерживает простой перебор элементов неуниверсальной коллекции. Другими словами, класс, который реализует этот интерфейс, можно использовать в цикле foreach для перебора всех элементов его коллекции. Создадим новый класс, реализующий интерфейс IEnumarable
для перечисления всех объектов Box
, находящихся в коллекции.
internal class BoxStorage : IEnumerable { Box[] boxes; public Box this[int index] { get { return boxes[index]; } } public BoxStorage(Box[] _boxes) { boxes = new Box[_boxes.Length]; for (int i = 0; i < _boxes.Length; i++) { boxes[i] = _boxes[i]; } } public IEnumerator GetEnumerator() { //метод интерфейса IEnumerable, который мы должны реализовать throw new NotImplementedException(); } }
В конструкторе BoxStorage
принимает массив объектов Box
. Все ссылки на элементы массива _boxes
копируются во внутренний массив boxes
. Интерфейс IEnumarable
содержит всего один метод — GetEnumerator
, который должен возвратить объект, реализующий интерфейс IEnumerator
(перечислитель).
Интерфейс IEnumerator
Реализуем интерфейс следующим образом:
//реализация интерфейса IEnumerator public class BoxEnumerator : IEnumerator { int currIndex = -1; Box[] enumBoxes; public BoxEnumerator(Box[] boxes) { enumBoxes = boxes; } public object Current => enumBoxes[currIndex]; public bool MoveNext() { currIndex++; if (currIndex < enumBoxes.Length) return true; return false; } public void Reset() { currIndex = -1; } }
Интерфейс IEnumerator
содержит:
- свойство
Current
, которое должно возвращать нам очередной элемент коллекции; - метод
MoveNext
, который возвращаетTrue
, в случае, если можно обратиться к очередному элементу коллекции; - метод
Reset
, который сбрасывает счётчик элементов на значение по умолчанию (-1
)
Для реализации интерфейса мы использовали в классе две переменные: currIndex
— индекс очередного элемента массива и enumBoxes
— собственно, сам массив элементы которого необходимо перечислить. Теперь нам необходимо дописать метод GetEnumerator
в классе BoxStorage
:
public IEnumerator GetEnumerator() { return new BoxEnumerator(boxes); }
Теперь наш класс может использоваться в цикле foreach
для перебора всех элементов массива, не раскрывая при этом сам массив в публичный доступ:
BoxStorage BoxStorage = new BoxStorage(new Box[] { new Box(){Height = 2, Length = 2, Width = 2 },//Объем 8 new Box(){Height = 1, Length = 1, Width = 1 },//Объем 1 new Box(){Height = 3, Length = 3, Width = 3 },//Объем 27 new Box(){Height = 4, Length = 4, Width = 4 } //Объем 64 }); foreach (Box box in BoxStorage) { Console.WriteLine($"Длина {box.Length} Ширина {box.Width} Высота {box.Height} Объем {box.Volume()}"); }
Результат:
Длина 1 Ширина 1 Высота 1 Объем 1
Длина 3 Ширина 3 Высота 3 Объем 27
Длина 4 Ширина 4 Высота 4 Объем 64
Итого
Используя интерфейсы IEnumerable
и IEnumerator
мы можем создавать собственные классы, позволяющие перечислять коллекции элементов определенного типа. В большинстве случаев нам достаточно тех классов, которые уже содержатся в .NET, однако, если вам необходимо реализовать собственный тип коллекции элементов, которая должна поддерживать перечисление с использованием цикла foreach, то в этом случае вам необходимо реализовать в своем проекте два интерфейса — IEnumerable
и IEnumerator
.