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

Итераторы и оператор yield

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

Итератор использует две специальных инструкции:

  • yield return: определяет возвращаемый элемент

  • yield break: указывает, что последовательность больше не имеет элементов

Рассмотрим небольшой пример:


Numbers numbers = new Numbers();
foreach (int n in numbers)
{
    Console.WriteLine(n);
}

class Numbers
{
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < 6; i++)
        {
            yield return i * i;
        }
    }
}

В классе Numbers метод GetEnumerator() фактически представляет итератор. С помощью оператора yield return возвращается некоторое значение (в данном случае квадрат числа).

В программе с помощью цикла foreach мы можем перебрать объект Numbers как обычную коллекцию. При получении каждого элемента в цикле foreach будет срабатывать оператор yield return, который будет возвращать один элемент и запоминать текущую позицию.

Благодаря итераторам мы можем пойти дальше и легко реализовать перебор числа в цикле foreach:


foreach(var n in 5) Console.WriteLine(n);
foreach (var n in -5) Console.WriteLine(n);

static class Int32Extension
{
    public static IEnumerator<int> GetEnumerator(this int number)
    {
        int k = (number > 0)? number: 0;
        for (int i = number - k; i <= k; i++) yield return i;
    }
}

В данном случае итератор реализован как метод расширения для типа int или System.Int32. В методе итератора фактически возвращаем все целочисленные значения от 0 до текущего числа. Консольный вывод:

0
1
2
3
4
5
-5
-4
-3
-2
-1
0

Другой пример: пусть у нас есть коллекция Company, которая представляет компанию и которая хранит в массиве personnel штат сотрудников - объектов Person. Используем оператор yield для перебора этой коллекции:


class Person
{
    public string Name { get; }
    public Person(string name) =>Name = name;
}
class Company
{
    Person[] personnel;
    public Company(Person[] personnel) => this.personnel = personnel;
    public int Length => personnel.Length;
    public IEnumerator<Person> GetEnumerator()
    {
        for (int i = 0; i < personnel.Length; i++)
        {
            yield return personnel[i];
        }
    }
}

Метод GetEnumerator() представляет итератор. И когда мы будем осуществлять перебор в объекте Company в цикле foreach, то будет идти обращение к вызову yield return personnel[i];. При обращении к оператору yield return будет сохраняться текущее местоположение. И когда метод foreach перейдет к следующей итерации для получения нового объекта, итератор начнет выполнения с этого местоположения.

Ну и в основной программе в цикле foreach выполняется собственно перебор, благодаря реализации итератора:


var people = new Person[] 
{ 
    new Person("Tom"), 
    new Person("Bob"), 
    new Person("Sam") 
};
var microsoft = new Company(people);

foreach(Person employee in microsoft)
{
    Console.WriteLine(employee.Name);
}

Хотя при реализации итератора в методе GetEnumerator() применялся перебор массива в цикле for, но это необязательно делать. Мы можем просто определить несколько вызовов оператора yield return:


public IEnumerator<Person> GetEnumerator()
{
    yield return personnel[0];
    yield return personnel[1];
    yield return personnel[2];
}

В этом случае при каждом вызове оператора yield return итератор также будет запоминать текущее местоположение и при последующих вызовах начинать с него.

Именованный итератор

Выше для создания итератора мы использовали метод GetEnumerator. Но оператор yield можно использовать внутри любого метода, только такой метод должен возвращать объект интерфейса IEnumerable. Подобные методы еще называют именованными итераторами.

Создадим такой именованный итератор в классе Company и используем его:


class Person
{
    public string Name { get; }
    public Person(string name) =>Name = name;
}
class Company
{
    Person[] personnel;
    public Company(Person[] personnel) => this.personnel = personnel;
    public int Length => personnel.Length;
    public IEnumerable<Person> GetPersonnel(int max)
    {
        for (int i = 0; i < max; i++)
        {
            if (i == personnel.Length)
            {
                yield break;
            }
            else
            {
                yield return personnel[i];
            }
        }
    }
}

Определенный здесь итератор - метод IEnumerable GetPersonnel(int max) в качестве параметра принимает количество выводимых объектов. В процессе работы программы может сложиться, что его значение будет больше, чем длина массива personnel. И чтобы не произошло ошибки, используется оператор yield break. Этот оператор прерывает выполнение итератора.

Применение итератора:


var people = new Person[]
{
    new Person("Tom"), 
    new Person("Bob"),
    new Person("Sam")
};
var microsoft = new Company(people);

foreach(Person employee in microsoft.GetPersonnel(5))
{
    Console.WriteLine(employee.Name);
}

Вызов microsoft.GetPersonnel(5) будет возвращать набор из не более чем 5 объектов Person. Но так как у нас всего три таких объекта, то в методе GetPersonnel после трех операций сработает оператор yield break.

Проверь себя

📋 Тест по теме «Итераторы и оператор yield» Тест по теме

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

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

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

_____ numbers = new Numbers();
foreach (int n in numbers)
{
    Console.WriteLine(n);
}

class Numbers
{
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < 6; i++)
        {
            yield return i * i;
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

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

foreach(var n in _____) Console.WriteLine(n);
foreach (var n in -5) Console.WriteLine(n);

static class Int32Extension
{
    public static IEnumerator<int> GetEnumerator(this int number)
    {
        int k = (number > 0)? number: 0;
        for (int i = number - k; i <= k; i++) yield return i;
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

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

class Person
{
    public string Name { _____; }
    public Person(string name) =>Name = name;
}
class Company
{
    Person[] personnel;
    public Company(Person[] personnel) => this.personnel = personnel;
    public int Length => personnel.Length;
    public IEnumerator<Person> GetEnumerator()
    {
        for (int i = 0; i < personnel.Length; i++)
        {
            yield return personnel[i];
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

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

var people = _____ Person[] 
{ 
    new Person("Tom"), 
    new Person("Bob"), 
    new Person("Sam") 
};
var microsoft = new Company(people);

foreach(Person employee in microsoft)
{
    Console.WriteLine(employee.Name);
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

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

public IEnumerator<Person> GetEnumerator()
{
    yield return _____[0];
    yield return personnel[1];
    yield return personnel[2];
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

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

_____ numbers = new Numbers();
foreach (int n in numbers)
{
    Console.WriteLine(n);
}

class Numbers
{
    public IEnumerator<int> _____()
    {
        for (int i = 0; i < 6; i++)
        {
            yield return i * i;
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

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

foreach(var n in 5) Console._____(n);
foreach (var n in -5) Console.WriteLine(n);

static class Int32Extension
{
    public static IEnumerator<int> GetEnumerator(this int _____)
    {
        int k = (number > 0)? number: 0;
        for (int i = number - k; i <= k; i++) yield return i;
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

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

public IEnumerator<Person> GetEnumerator()
    yield return personnel[0];
    yield return personnel[2];
{
}
    yield return personnel[1];
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Итераторы и оператор yield»
+30 XP

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

Numbers numbers = new Numbers();
foreach (int n in numbers)
{
    Console.WriteLine(n);
}

class Numbers
{
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < 6; i++)
        {
            yield return i * i;
        }
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Итераторы и оператор yield»
+30 XP

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

foreach(var n in 5) Console.WriteLine(n);
foreach (var n in -5) Console.WriteLine(n);

static class Int32Extension
{
    public static IEnumerator<int> GetEnumerator(this int number)
    {
        int k = (number > 0)? number: 0;
        for (int i = number - k; i <= k; i++) yield return i;
    }
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Итераторы и оператор yield»
+50 XP

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

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

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

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

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

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