Какие операторы работают как итераторы в C#
Что проверяют
Знаете ли вы, что yield превращает метод в конечный автомат с отложенным выполнением, и понимаете ли последствия этой ленивости. Следом обычно спрашивают, чем отложенные методы LINQ отличаются от немедленных и почему IEnumerable опасно перечислять дважды.
Как отвечать
Главные конструкции:
yield return: отдаёт очередной элемент и приостанавливает метод до следующегоMoveNext(). Такой метод возвращаетIEnumerable<T>илиIEnumerator<T>, а с C# 8 ещё иIAsyncEnumerable<T>;yield break: завершает последовательность, аналогreturnдля итератора;foreach: потребитель итератора. Работает по шаблону: хватает методаGetEnumerator()сMoveNext()иCurrent, интерфейс не обязателен. В конце вызываетDispose(), в том числе приbreak, и тогда отрабатывают блокиfinallyвнутри итератора;await foreach: асинхронный переборIAsyncEnumerable<T>, например постраничное чтение из API или базы.
Под капотом компилятор генерирует скрытый класс, который хранит локальные переменные и номер состояния. Поэтому тело метода не выполняется, пока перебор не начался. Проверка аргументов внутри итератора сработает не при вызове, а при первом MoveNext(), и её выносят во внешний метод, а сам итератор делают локальной функцией.
На том же механизме стоит LINQ to Objects: Where, Select, Take, SelectMany ленивые, а ToList, Count, First, Sum выполняются сразу. OrderBy тоже отложен, но при первом обращении читает всю последовательность.
Ограничения: yield нельзя писать в лямбдах и анонимных методах, у итератора не бывает параметров ref и out, а yield return не ставится в try с блоком catch.
Где спотыкаются
Перечисляют IEnumerable несколько раз. Каждый проход заново выполняет итератор, и если внутри запрос к базе или чтение файла, работа повторяется. Результат материализуют через ToList().
Ждут исключение о неверном аргументе в момент вызова метода, а оно прилетает позже, при первом переборе.
Меняют коллекцию во время перебора и получают InvalidOperationException: перечислитель List<T> проверяет версию коллекции на каждом шаге.