Что такое итератор и ключевое слово yield в C#?

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

Ответ

Итератор — это паттерн, позволяющий последовательно обходить элементы коллекции, не раскрывая её внутреннее устройство. В C# итераторы реализуются через интерфейсы IEnumerable (предоставляет перечислитель) и IEnumerator (непосредственно выполняет перебор).

Ключевое слово yield — это синтаксический сахар, который значительно упрощает создание итераторов. Компилятор автоматически генерирует класс, реализующий IEnumerable/IEnumerator, с сохранением состояния между шагами.

Основные понятия:

  • yield return — возвращает следующий элемент последовательности и приостанавливает выполнение метода до следующего вызова MoveNext().
  • yield break — завершает последовательность.
  • Ленивое выполнение: Элементы вычисляются только в момент запроса, что экономит память и CPU.

Пример: Генерация последовательности и фильтрация "на лету"

// Итератор, генерирующий бесконечную последовательность чисел Фибоначчи
public static IEnumerable<long> FibonacciSequence()
{
    long a = 0, b = 1;
    while (true)
    {
        yield return a; // Приостановка и возврат значения
        long temp = a;
        a = b;
        b = temp + b;
    }
}

// Итератор для фильтрации (пайплайн)
public static IEnumerable<int> GetEvenNumbers(IEnumerable<int> source)
{
    foreach (int number in source)
    {
        if (number % 2 == 0)
        {
            yield return number; // Возвращаем только чётные
        }
        // Если число нечётное, просто пропускаем итерацию
    }
}

// Использование:
// 1. Берём первые 10 чисел Фибоначчи
var firstTenFib = FibonacciSequence().Take(10);
// 2. Фильтруем их, оставляя только чётные
var evenFib = GetEvenNumbers(firstTenFib);
// 3. Материализуем результат (здесь происходит реальное вычисление)
foreach (var num in evenFib)
{
    Console.WriteLine(num); // Выведет: 0, 2, 8, 34...
}

Важные ограничения:

  • Методы с yield не могут содержать return (кроме yield return/break).
  • yield return нельзя использовать внутри try-catch, но можно внутри try-finally (финальная часть выполнится при завершении перебора).