Какие методы включает интерфейс IEnumerator в C#?

«Какие методы включает интерфейс IEnumerator в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Интерфейс System.Collections.IEnumerator (необобщенный) и его обобщенный аналог System.Collections.Generic.IEnumerator<T> определяют контракт для последовательного перебора элементов коллекции. Это основа работы цикла foreach.

Основные методы интерфейса IEnumerator:

  1. bool MoveNext()

    • Назначение: Перемещает перечислитель к следующему элементу коллекции.
    • Возвращаемое значение: true, если перечислитель успешно перемещен к следующему элементу; false, если перечислитель достиг конца коллекции.
    • Важно: Перед первым вызовом MoveNext() перечислитель позиционирован перед первым элементом коллекции. Первый вызов перемещает его на первый элемент.
  2. void Reset()

    • Назначение: Устанавливает перечислитель в его начальное положение, которое находится перед первым элементом коллекции.
    • Особенность: Не все реализации поддерживают этот метод. При вызове у неподдерживающего enumerator'а выбрасывается NotSupportedException. В современном коде (IEnumerator<T>) его использование считается устаревшим.
  3. object Current { get; } (Свойство)**

    • Назначение: Возвращает текущий элемент коллекции.
    • Особенности:
      • Свойство следует вызывать только после успешного вызова MoveNext().
      • Если перечислитель позиционирован перед первым элементом или после последнего, поведение свойства Current не определено (обычно бросается исключение InvalidOperationException).
      • При каждом обращении возвращает один и тот же объект, пока не будет вызван MoveNext().

Обобщенный интерфейс IEnumerator<T> наследует IEnumerator и IDisposable и добавляет:

  • T Current { get; } – типобезопасное свойство, возвращающее элемент типа T.
  • void Dispose() – для освобождения ресурсов (например, закрытия открытого файла или соединения с БД, использованного при перечислении).

Практический пример: Реализация простого перечислителя:

public class CountdownEnumerator : IEnumerator<int>
{
    private int _current;
    private readonly int _start;

    public CountdownEnumerator(int start)
    {
        _start = start;
        Reset(); // Инициализируем позицию перед первым элементом
    }

    public int Current => _current;
    object IEnumerator.Current => Current;

    public bool MoveNext()
    {
        if (_current > 0)
        {
            _current--;
            return true;
        }
        return false; // Достигнут конец
    }

    public void Reset()
    {
        _current = _start + 1; // Позиция ПЕРЕД первым элементом (start)
    }

    public void Dispose()
    {
        // В данном примере нечего освобождать
    }
}
// Использование
var enumerator = new CountdownEnumerator(3);
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current); // Выведет: 3, 2, 1
}

Итог: MoveNext() и Current — это минимально необходимые методы для поддержки перечисления. Reset() используется редко, а Dispose() в IEnumerator<T> важен для корректного управления ресурсами.