← Назад к вопросам

Что такое паттерн проектирования итератор?

2.0 Middle🔥 202 комментариев
#ООП и паттерны проектирования

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн проектирования Итератор

Итератор — это поведенческий паттерн проектирования, который предоставляет механизм последовательного доступа к элементам коллекции (агрегированного объекта) без раскрытия его внутренней структуры. Он инкапсулирует логику обхода, позволяя клиентскому коду работать с различными типами коллекций единообразно. В основе паттерна лежит идея вынесения ответственности за обход в отдельный объект — итератор.

Основные цели и принципы паттерна

  1. Разделение ответственности: Коллекция отвечает только за хранение данных, а итератор — за обход.
  2. Инкапсуляция: Скрывает внутреннюю реализацию коллекции (массив, связный список, дерево).
  3. Унифицированный интерфейс: Клиент работает с разными коллекциями через единый интерфейс итератора.
  4. Поддержка нескольких обходов: Для одной коллекции можно создать несколько независимых итераторов.

Стандартная реализация в C# (интерфейсы IEnumerable и IEnumerator)

В C# паттерн итератор встроен в язык через два ключевых интерфейса пространства имён System.Collections:

  • IEnumerable: Определяет метод GetEnumerator(), который возвращает итератор. Коллекция, реализующая этот интерфейс, считается "перечислимой".
  • IEnumerator: Предоставляет методы для обхода: MoveNext() (переход к следующему элементу), Current (получение текущего элемента), Reset() (сброс к началу).

Пример реализации с нуля:

// Кастомная коллекция, реализующая IEnumerable
public class BooksCollection : IEnumerable<Book>
{
    private Book[] _books;

    public BooksCollection(Book[] books)
    {
        _books = books;
    }

    // Возвращает итератор
    public IEnumerator<Book> GetEnumerator()
    {
        return new BooksIterator(_books);
    }

    // Явная реализация для совместимости
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

// Кастомный итератор, реализующий IEnumerator<Book>
public class BooksIterator : IEnumerator<Book>
{
    private Book[] _books;
    private int _position = -1; // Начальная позиция перед первым элементом

    public BooksIterator(Book[] books)
    {
        _books = books;
    }

    public bool MoveNext()
    {
        _position++;
        return (_position < _books.Length);
    }

    public Book Current
    {
        get
        {
            try
            {
                return _books[_position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }

    object System.Collections.IEnumerator.Current => Current;

    public void Reset() => _position = -1;

    public void Dispose() { }
}

// Использование
var books = new Book[] { new Book("CLR via C#"), new Book("Clean Code") };
var collection = new BooksCollection(books);

foreach (var book in collection) // foreach неявно использует GetEnumerator()
{
    Console.WriteLine(book.Title);
}

Использование yield return для упрощения

C# предоставляет синтаксический сахар через ключевое слово yield return, которое автоматически генерирует итератор. Это делает код более лаконичным:

public class BooksCollection : IEnumerable<Book>
{
    private Book[] _books;

    public IEnumerator<Book> GetEnumerator()
    {
        for (int i = 0; i < _books.Length; i++)
        {
            yield return _books[i]; // Компилятор создаёт класс-итератор
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerator.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Преимущества паттерна Итератор

  • Упрощает клиентский код: Клиенту не нужно знать детали реализации коллекции.
  • Поддержка полиморфного обхода: Итератор позволяет обходить разные структуры данных (списки, деревья, графы) с помощью одного интерфейса.
  • Параллельные обходы: Можно создать несколько независимых итераторов для одной коллекции.
  • Отложенное выполнение (Lazy Evaluation): Элементы могут извлекаться по мере необходимости, особенно с использованием yield return.

Недостатки

  • Избыточность для простых коллекций: Для массивов или списков использование итератора может быть излишним.
  • Ограничения производительности: В некоторых сценариях прямой доступ к коллекции может быть быстрее (хотя в современных версиях C# оптимизации нивелируют это).

Практическое применение в C#

  • LINQ to Objects: Все расширяющие методы LINQ (Where, Select) построены на итераторах.
  • Асинхронные итераторы: C# 8.0+ поддерживает IAsyncEnumerable<T> для асинхронного обхода.
  • Кастомные структуры данных: Реализация итераторов для деревьев, графов или потоковых данных.

Итератор — это фундаментальный паттерн, который в C# глубоко интегрирован в язык и стандартные библиотеки. Он обеспечивает гибкость, поддерживая принцип единственной ответственности и открывая возможности для расширяемости, таких как LINQ и асинхронные операции.