Коллекции объектов. Интерфейсы IEnumerable и IEnumerator

В подавляющем большинстве случаев, возможности платформы .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()}");
}

Результат:

Длина 2 Ширина 2 Высота 2 Объем 8
Длина 1 Ширина 1 Высота 1 Объем 1
Длина 3 Ширина 3 Высота 3 Объем 27
Длина 4 Ширина 4 Высота 4 Объем 64

Итого

Используя интерфейсы IEnumerable и IEnumerator мы можем создавать собственные классы, позволяющие перечислять коллекции элементов определенного типа. В большинстве случаев нам достаточно тех классов, которые уже содержатся в .NET, однако, если вам необходимо реализовать собственный тип коллекции элементов, которая должна поддерживать перечисление с использованием цикла foreach, то в этом случае вам необходимо реализовать в своем проекте два интерфейса — IEnumerable и IEnumerator.

Подписаться
Уведомить о
guest
2 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии