Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Выберите итератор, когда вы хотите вернуть последовательность, элементы которой вызывающий код может обрабатывать по одному, не создавая предварительно полный результат в коллекции. Этот подход подходит для сгенерированных значений, фильтров с настраиваемым потоком управления и последовательностей, чтение которых вызывающий код может прекратить раньше. По сравнению с завершенной коллекцией итератор меняет момент выполнения работы, а не только способ его написания.
Tip
В этой статье описаны синхронные итераторы для разработчиков, которые знают методы, циклы и коллекции. Полный синтаксис и ограничения см. в справочнике по yieldоператору.
Метод-итератор создает последовательность значений по одному за раз. В этой статье рассматриваются методы итератора, возвращающие IEnumerable<T>, значения которого обычно используются вызывающим кодом с инструкцией foreach.
Создавайте элементы с помощью yield return
Используйте yield return, чтобы указать следующий элемент последовательности. Следующий итератор выбирает допустимые значения температуры с датчика. В этом сценарии -1 помечает конец данных датчика.
int[] readings = [21, 27, 24, -1, 19];
IEnumerable<int> acceptedReadings =
SelectTemperatures(readings, maximum: 25);
Console.WriteLine("Sequence created.");
foreach (int temperature in acceptedReadings)
{
Console.WriteLine($"Accepted: {temperature}°C");
}
static IEnumerable<int> SelectTemperatures(
IEnumerable<int> readings,
int maximum)
{
foreach (int reading in readings)
{
Console.WriteLine($"Checking {reading}°C");
if (reading == -1)
{
yield break;
}
if (reading <= maximum)
{
yield return reading;
}
}
}
SelectTemperatures возвращает значение IEnumerable<int>, представляющее последовательность значений int . Каждый yield return reading предоставляет одно значение циклу foreach. После того как вызывающий объект обрабатывает это значение, итератор переходит к следующему входному значению.
Используйте обобщённую IEnumerable<T> форму для большей части современного кода C#. Аргумент типа, например int, сообщает вызывающему коду, какой тип элемента содержит последовательность.
Завершите последовательность с помощью yield break
Используйте yield break, чтобы завершить итератор, прежде чем управление достигнет конца метода. В этом примере сторожевое значение -1 означает, что больше нет доступных показаний:
if (reading == -1)
{
yield break;
}
Итератор также заканчивается естественным образом, когда выполнение достигает конца его тела. Используйте yield break, когда условие должно немедленно остановить создание элементов.
Использовать итератор с foreach
Итератор предоставляет ту же IEnumerable<T> структуру, что и многие коллекции .NET. Вызывающие могут использовать foreach , не зная, как последовательность создает свои элементы:
foreach (int temperature in SelectTemperatures(readings, 25))
{
Console.WriteLine($"Accepted: {temperature}°C");
}
Вызывающая сторона запрашивает каждый элемент по мере продвижения цикла. Итератор сохраняет свою позицию между запросами и продолжает работу после предыдущего yield return.
Понимание отложенного создания элементов
Вызов метода итератора не запускает его тело немедленно. Метод начинает создавать элементы, когда вызывающий код начинает перечисление. Это поведение называется отложенным выполнением.
Пример выводит Sequence created. перед любыми сообщениями Checking .... Итератор начинает проверку показаний только тогда, когда цикл foreach запрашивает свой первый элемент.
Отложенное выполнение имеет практические последствия:
- Вызывающая сторона может начать обработку, прежде чем итератор выдаст все элементы.
- Итератор может завершиться раньше с помощью
yield break. - Вызывающий объект, который никогда не перечисляет последовательность, не запускает тело итератора.
- Каждое новое перечисление снова выполняет итерацию по итератору.
Ясно укажите отложенное выполнение в назначении и документации метода. Читатели не могут сделать вывод о времени выполнения по одному лишь IEnumerable<T>, поскольку этот интерфейс могут предоставлять как итераторы, так и коллекции. Сохраняйте создание элементов свободным от несвязанных побочных эффектов, так как вызывающий код может перечислять последовательность позже или несколько раз. Диагностические сообщения примера иллюстрируют этот тайминг.
Выбор итератора или коллекции
Возвращает итератор, когда значения естественным образом поступают по одному или когда вызывающая сторона не нуждается в полном результате немедленно. Возвращает готовую коллекцию, когда вызывающему коду нужны стабильный снимок состояния, индексированный доступ или повторное перечисление без пересчета элементов.
Оба подхода могут возвращать одни и те же элементы. Итератор оставляет создание элементов под управлением вызывающей стороны; метод, создающий коллекцию, завершает эту работу перед возвратом.
Используйте обычный цикл, если вы хотите выполнить действие для каждого элемента без возврата последовательности. Для стандартного фильтра или преобразования предпочитайте операцию LINQ, например Where или Select; напишите итератор, если пользовательская логика производства элементов более понятна в виде последовательности инструкций.
Методы итератора составляются с помощью LINQ, так как оба используют IEnumerable<T>. Например, вызывающий объект может добавить другой фильтр:
var comfortableReadings = SelectTemperatures(readings, 25)
.Where(static temperature => temperature >= 18);
Итератор и операция LINQ остаются отложенными, пока код не выполнит перечисление comfortableReadings.
Сведения об асинхронных последовательностях см. в разделе Создание и использование асинхронных потоков. Для пользовательских шаблонов перечисления и всех yield ограничений используйте справочник по языку.