.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День сто девяносто пятый. #ИнтересныйКод
Ленивый итератор с помощью yield return
Следующий код показывает метод Fibonacci(), возвращающий бесконечную последовательность чисел Фибоначчи, и использование этого метода для вывода чисел до нужного предела:
static IEnumerable<int> Fibonacci()
{
int current = 0;
int next = 1;
// бесконечный цикл? Только если продолжать запрашивать значения
while (true)
{
yield return current;
int oldCurrent = current;
current = next;
next += oldCurrent;
}
}
static void Main()
{
foreach (var value in Fibonacci())
{
Console.WriteLine(value);
// условие остановки цикла
if (value > 1000) { break; }
}
}
Как реализовать что-то подобное без итераторов? Можно изменить метод для создания List<int> и заполнять его, пока не достигнут предел. Но этот список может быть большим, если предел велик, и почему метод, который знает детали последовательности Фибоначчи, также должен знать, когда остановиться? Предположим, иногда нужно остановиться по значению текущего числа, а иногда по количеству выведенных чисел или через некоторое время.
Можно избежать создания списка, печатая значение в цикле, но это делает метод Fibonacci() еще более тесно связанным с тем, что вы хотите сделать со значениями прямо сейчас. Что если вы хотите складывать значения, а не печатать их? Писать другой метод? Это ужасное нарушение разделения ответственности.
Решение через итератор - это именно то, что вам нужно: представление бесконечной последовательности и всё. Вызывающий код может выполнять итерацию по своему усмотрению (по крайней мере, до переполнения int) и использовать значения, как хочет.
Реализация последовательности Фибоначчи вручную проста. Не нужно сохранять много данных состояния (только два предыдущих числа), а управлять логикой выполнения довольно просто (здесь только один оператор yield return). Но если логика усложняется, реализация её в коде становится крайне непростой. Таким образом, использование итераторов с yield return – это мощный инструмент, сильно упрощающий разработку.
Кроме того, компилятор также достаточно умён, чтобы правильно обрабатывать блоки finally, что не так очевидно, как кажется (об этом далее).

Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 2.