Полное руководство по языку программирования С# 14 и платформе .NET 10 / Коллекции

Интерфейсы IEnumerable и IEnumerator

Начальный ⏱ 4 мин чтения 👁 2 🕓 обновлено 11.08.2026

Как мы увидели, основной для большинства коллекций является реализация интерфейсов IEnumerable и IEnumerator. Благодаря такой реализации мы можем перебирать объекты в цикле foreach:


foreach(var item in перечислимый_объект)
{
	
}

Перебираемая коллекция должна реализовать интерфейс IEnumerable.

Интерфейс IEnumerable имеет метод, возвращающий ссылку на другой интерфейс - перечислитель:


public interface IEnumerable
{
	IEnumerator GetEnumerator();
}

А интерфейс IEnumerator определяет функционал для перебора внутренних объектов в контейнере:


public interface IEnumerator
{
	bool MoveNext(); // перемещение на одну позицию вперед в контейнере элементов
	object Current {get;}  // текущий элемент в контейнере
	void Reset(); // перемещение в начало контейнера
}

Метод MoveNext() перемещает указатель на текущий элемент на следующую позицию в последовательности. Если последовательность еще не закончилась, то возвращает true. Если же последовательность закончилась, то возвращается false.

Свойство Current возвращает объект в последовательности, на который указывает указатель.

Метод Reset() сбрасывает указатель позиции в начальное положение.

Каким именно образом будет осуществляться перемещение указателя и получение элементов зависит от реализации интерфейса. В различных реализациях логика может быть построена различным образом.

Например, без использования цикла foreach перебирем массив с помощью интерфейса IEnumerator:


using System.Collections;

string[] people = {"Tom", "Sam", "Bob"};

IEnumerator peopleEnumerator = people.GetEnumerator(); // получаем IEnumerator
while (peopleEnumerator.MoveNext())   // пока не будет возвращено false
{
    string item = (string)peopleEnumerator.Current; // получаем элемент на текущей позиции
    Console.WriteLine(item);
}
peopleEnumerator.Reset(); // сбрасываем указатель в начало массива

Реализация IEnumerable и IEnumerator

Рассмотрим простешую реализацию IEnumerable на примере:


using System.Collections;

Week week = new Week();
foreach (var day in week)
{
    Console.WriteLine(day);
}

class Week : IEnumerable
{
    string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday",
                         "Friday", "Saturday", "Sunday" };
    public IEnumerator GetEnumerator() => days.GetEnumerator();
}

В данном случае класс Week, который представляет неделю и хранит все дни недели, реализует интерфейс IEnumerable. Однако в данном случае мы поступили очень просто - вместо реализации IEnumerator мы просто возвращаем в методе GetEnumerator объект IEnumerator для массива.


public IEnumerator GetEnumerator() => days.GetEnumerator();

Благодаря этому мы можем перебрать все дни недели в цикле foreach.

В то же время стоит отметить, что для перебора коллекции через foreach в принципе необязательно реализовать интерфейс IEnumerable. Достаточно в классе определить публичный метод GetEnumerator, который бы возвращал объект IEnumerator. Например:


class Week
{
	string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday", 
						"Friday", "Saturday", "Sunday" };
	public IEnumerator GetEnumerator() =>days.GetEnumerator();
}

Однако это было довольно просто - мы просто используем уже готовый перчислитель массива. Однако, возможно, потребуется задать свою собственную логику перебора объектов. Для этого реализуем интерфейс IEnumerator:


using System.Collections;

class WeekEnumerator : IEnumerator
{
    string[] days;
    int position = -1;
    public WeekEnumerator(string[] days) => this.days = days;
    public object Current
    {
        get
        {
            if (position == -1 || position >= days.Length)
                throw new ArgumentException();
            return days[position];
        }
    }
    public bool MoveNext()
    {
        if (position < days.Length - 1)
        {
            position++;
            return true;
        }
        else
            return false;
    }
    public void Reset() => position = -1;
}
class Week
{
    string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday",
                            "Friday", "Saturday", "Sunday" };
    public IEnumerator GetEnumerator() => new WeekEnumerator(days);
}

Здесь теперь класс Week использует не встроенный перечислитель, а WeekEnumerator, который реализует IEnumerator.

Ключевой момент при реализации перечислителя - перемещения указателя на элемент. В классе WeekEnumerator для хранения текущей позиции определена переменная position. Следует учитывать, что в самом начале (в исходном состоянии) указатель должен указывать на позицию условно перед первым элементом. Когда будет производиться цикл foreach, то данный цикл вначале вызывает метод MoveNext и фактически перемещает указатель на одну позицию в перед и только затем обращается к свойству Current для получения элемента в текущей позиции.

Затем в программе мы можем аналогичным образом перебирать объект Week с помощью цикла foreach:


Week week = new Week();
foreach(var day in week)
{
	Console.WriteLine(day);
}

Обобщенная версия IEnumerator

В примерах выше использовались необобщенные версии интерфейсов, однако мы также можем использовать их обобщенные двойники:


using System.Collections;

class WeekEnumerator : IEnumerator<string>
{
    string[] days;
    int position = -1;
    public WeekEnumerator(string[] days) => this.days = days;
    public string Current
    {
        get
        {
            if (position == -1 || position >= days.Length)
                throw new ArgumentException();
            return days[position];
        }
    }
    object IEnumerator.Current => Current;
    public bool MoveNext()
    {
        if (position < days.Length - 1)
        {
            position++;
            return true;
        }
        else
            return false;
    }
    public void Reset() => position = -1;
    public void Dispose() { }
}
class Week
{
    string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday",
                            "Friday", "Saturday", "Sunday" };
    public IEnumerator<string> GetEnumerator() => new WeekEnumerator(days);
}

В данном случае реализуем интерфейс IEnumerator<string>, соответственно в свойстве Current нам надо возвратить объект string. В этом случае при переборе в цикле foreach перебираемые объекты будут автоматически представлять тип string:


Week week = new Week();
foreach (string day in week)
{
    Console.WriteLine(day);
}

Проверь себя

📋 Тест по теме «Интерфейсы IEnumerable и IEnumerator» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____(var item in перечислимый_объект)
{
	
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

public interface _____
{
	IEnumerator GetEnumerator();
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

public interface IEnumerator
{
	_____ MoveNext(); // перемещение на одну позицию вперед в контейнере элементов
	object Current {get;}  // текущий элемент в контейнере
	void Reset(); // перемещение в начало контейнера
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

using System.Collections;

string[] people = {"_____", "Sam", "Bob"};

IEnumerator peopleEnumerator = people.GetEnumerator(); // получаем IEnumerator
while (peopleEnumerator.MoveNext())   // пока не будет возвращено false
{
    string item = (string)peopleEnumerator.Current; // получаем элемент на текущей позиции
    Console.WriteLine(item);
}
peopleEnumerator.Reset(); // сбрасываем указатель в начало массива
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

using System.Collections;

Week week = _____ Week();
foreach (var day in week)
{
    Console.WriteLine(day);
}

class Week : IEnumerable
{
    string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday",
                         "Friday", "Saturday", "Sunday" };
    public IEnumerator GetEnumerator() => days.GetEnumerator();
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

_____(var _____ in перечислимый_объект)
{
	
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

public interface IEnumerable
{
	_____ _____();
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

}
{
foreach(var item in перечислимый_объект)
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Интерфейсы IEnumerable и IEnumerator»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Интерфейсы IEnumerable и IEnumerator», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

foreach(var item in перечислимый_объект)
{
	
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Интерфейсы IEnumerable и IEnumerator»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

public interface IEnumerable
{
	IEnumerator GetEnumerator();
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Интерфейсы IEnumerable и IEnumerator»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Интерфейсы IEnumerable и IEnumerator»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Интерфейсы IEnumerable и IEnumerator». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.