Паттерны проектирования
4. Паттерн «Итератор» (Iterator).
Практически любое приложение в той или иной мере работает с коллекциями данных. В некоторых случаях для обработки данных коллекции используется специфический интерфейс конкретных коллекций, но в большинстве случаев доступ к элементам осуществляется через специальный абстрактный слой — итератор.
Назначение: Итераторы предоставляют абстрактный интерфейс для доступа к содержимому составных объектов, не раскрывая клиентам их внутреннюю структуру. В результате получается чёткое разделение ответственности: клиенты получают возможность работать с разными коллекциями унифицированным образом, а классы коллекций становятся проще за счёт того, что ответственность за перебор элементов возлагается на отдельную сущность.
Классическая схема паттерна «Итератор» и реализация паттерна в .Net представлены на рисунке ниже:
-
Iterator (IEnumerator<T>) — определяет интерфейс итератора. В .Net имеет методы: MoveNext (переход на следующий элемент, возвращает false, если достигнут конец последовательности), Current (возвращает текущий элемент) и Reset (возвращает итератор к началу коллекции, реализуется не всегда).-
Aggregate (IEnumerable<T>) — коллекция, по которой можетперемещаться итератор. В .Net интерфейс имеет единственный метод
GetEnumerator для получения экземпляра итератора.-
ConcreteAggregate1 (List<T>) — конкретная реализация коллекции;-
ConcreteIterator (List<T>.Enumerator) — конкретная реализацияитератора для определённой коллекции.
Итераторы в .NET являются однонаправленными и только для чтения. То есть при изменении коллекции последующий вызов
MoveNext итератора выбросит InvalidOperationException. По той же причине нельзя изменять элементы коллекции в цикле foreach. Цикл foreach является универсальным инструментом для обработки коллекций/последовательностей. Компилятор преобразует его в (в упрощённом варианте) вызов метода MoveNext и обращение к свойству Current в цикле while. Для поддержки цикла foreach не обязательно наличие интерфейса IEnumerable. Достаточно, чтобы класс коллекции содержал метод GetEnumerator, который будет возвращать тип с методом MoveNext, возвращающим bool, и свойством Current.Блоки итераторов
Процесс создания итераторов вручную довольно утомителен и включает управление состоянием и перемещением по элементам коллекции при вызове
MoveNext. С помощью блока итераторов реализовать итератор существенно проще:public static IEnumerator<int> CustomArrayIterator(this int[] array) {
foreach (var n in array) { yield return n; }
}
Блок итераторов преобразуется компилятором языка C# в конечный автомат с несколькими состояниями, соответствующими начальному положению итератора (когда он указывает на –1-й элемент), конечному положению (когда итератор прошёл все элементы) и «среднему» положению, при котором он указывает на определённый элемент.С помощью блока итераторов можно создавать итераторы для своих коллекций, существующих коллекций или вообще для внешних ресурсов, таких как файлы. Для этого достаточно открыть файл в начале метода и возвращать прочитанные блоки данных с помощью
yield return.Итераторы, полученные с помощью блока итераторов, являются «ленивыми»: их тело исполняется не в момент вызова метода, а при переборе элементов с помощью метода
MoveNext. Кроме того, блоки итераторов можно использовать для создания генераторов последовательностей. Например, «бесконечного» генератора чисел Фибоначчи.
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 4.