- Статья
- Чтение занимает 2 мин
Следующие операторы многократно выполняют инструкцию или блок инструкций:
- Оператор
for: выполняет тело, а заданное логическое выражение принимает значениеtrue. - Оператор
foreach: перечисляет элементы коллекции и выполняет тело для каждого элемента коллекции. - Оператор
do: условно выполняет тело один или несколько раз. - Оператор
while: условно выполняет тело ноль или более раз.
Цикл можно прервать в любой момент в теле оператора итерации с помощью оператора break. Кроме того, можно перейти к следующей итерации в цикле с помощью оператора continue.
Инструкция for
Оператор for выполняет оператор или блок операторов, пока определенное логическое выражение равно значению true. В следующем примере показана инструкция for, выполняющая тело пока целочисленный счетчик меньше трех:
for (int i = 0; i < 3; i++)
{
Console.Write(i);
}
// Output:
// 012
В предыдущем примере показаны элементы оператора for:
-
Раздел инициализатора, который выполняется только один раз перед входом в цикл. Как правило, в этом разделе объявляется и инициализируется локальная переменная цикла. Доступ к объявленной переменной извне оператора
forневозможен.В разделе инициализатора в предыдущем примере объявляется и инициализируется целочисленная переменная-счетчик:
int i = 0 -
Раздел условия, в котором определяется, следует ли выполнять следующую итерацию в цикле. Если для него получено значение
trueили значение отсутствует, выполняется следующая итерация; в противном случае цикл завершается. Раздел условия должен быть логическим выражением.В разделе условия в предыдущем примере проверяется, меньше ли трех значение счетчика.
i < 3 -
Раздел итератора, который определяет, что происходит после каждого выполнения тела цикла.
Раздел итератора в предыдущем примере увеличивает значение счетчика:
i++ -
Тело цикла которое должно быть оператором или блоком операторов.
Раздел итератора может содержать ноль или более следующих выражений оператора, разделенных запятыми:
- префиксное или постфиксное выражение приращения, такое как
++iилиi++ - префиксное или постфиксное выражение декремента, такое как
--iилиi-- - присваивание
- вызов метода
- выражение await
- создание объекта с помощью оператора new
Если переменная цикла не объявлена в разделе инициализатора, в разделе инициализатора можно также использовать ноль или более выражений из предыдущего списка. В следующем примере показано несколько менее распространенных вариантов использования разделов инициализатора и итератора: присваивание значения внешней переменной цикла в разделе инициализатора, вызов метода в разделах инициализатора и итератора и изменение значения двух переменных в разделе итератора.
int i;
int j = 3;
for (i = 0, Console.WriteLine($"Start: i={i}, j={j}"); i < j; i++, j--, Console.WriteLine($"Step: i={i}, j={j}"))
{
//...
}
// Output:
// Start: i=0, j=3
// Step: i=1, j=2
// Step: i=2, j=1
Все разделы оператора for необязательны. Например, в следующем коде определяется бесконечный цикл for:
for ( ; ; )
{
//...
}
Инструкция foreach
Оператор foreach выполняет оператор или блок операторов для каждого элемента в экземпляре типа, который реализует интерфейс System.Collections.IEnumerable или System.Collections.Generic.IEnumerable<T>, как показано в следующем примере.
var fibNumbers = new List<int> { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibNumbers)
{
Console.Write($"{element} ");
}
// Output:
// 0 1 1 2 3 5 8 13
Оператор foreach не ограничен этими типами. Его можно использовать с экземпляром любого типа, который удовлетворяет следующим условиям:
- Тип имеет открытый метод без параметров
GetEnumerator. Начиная с C# 9.0 методGetEnumeratorможет быть методом расширения типа. - тип возвращаемого значения метода
GetEnumeratorдолжен содержать открытое свойствоCurrentи открытый методMoveNextбез параметров с типом возвращаемого значенияbool.
В следующем примере показано использование оператора foreach с экземпляром типа System.Span<T>, который не реализует интерфейс:
Span<int> numbers = new int[] { 3, 14, 15, 92, 6 };
foreach (int number in numbers)
{
Console.Write($"{number} ");
}
// Output:
// 3 14 15 92 6
Начиная с версии C# 7.3, если свойство перечислителя Current возвращает ссылочное возвращаемое значение (ref T, где T — это тип элемента коллекции), вы можете объявить переменную итерации с модификатором ref или ref readonly, как показано в следующем примере.
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
foreach (ref readonly var item in storage)
{
Console.Write($"{item} ");
}
// Output:
// 0 1 2 3 4 5 6 7 8 9
Если оператор foreach применяется к null, возникает исключение NullReferenceException. Если исходная коллекция инструкции foreach пуста, тело оператора foreach не выполняется и пропускается.
await foreach
Начиная с C# 8.0, можно применять оператор await foreach для использования асинхронного потока данных, то есть типа коллекции, реализующего интерфейс IAsyncEnumerable<T>. Каждую итерацию цикла можно приостановить, пока будет осуществляться асинхронное извлечение следующего элемента. В следующем примере показано использование оператора await foreach.
await foreach (var item in GenerateSequenceAsync())
{
Console.WriteLine(item);
}
Оператор await foreach можно также использовать с экземпляром любого типа, который удовлетворяет следующим условиям:
- Тип имеет открытый метод без параметров
GetAsyncEnumerator. Этот метод может быть методом расширения типа. - Тип возвращаемого значения метода
GetAsyncEnumeratorимеет открытое свойствоCurrentи открытый метод без параметровMoveNextAsync, тип возвращаемого значения которого —Task<bool>,ValueTask<bool>или любой другой подтверждающий ожидание тип, метод ожидания которогоGetResultвозвращает значениеbool.
Элементы потока по умолчанию обрабатываются в захваченном контексте. Чтобы отключить захват контекста, используйте метод расширения TaskAsyncEnumerableExtensions.ConfigureAwait. Дополнительные сведения о контекстах синхронизации и захвате текущего контекста см. в статье Использование асинхронного шаблона, основанного на задачах. Дополнительные сведения об асинхронных потоках см. в разделе Асинхронные потоки статьи Новые возможности в C# 8.0.
Тип переменной итерации
Можно использовать ключевое слово var, чтобы компилятор мог определить тип переменной итерации в операторе foreach, как показано в следующем коде:
foreach (var item in collection) { }
Можно также явно указать тип переменной итерации, как показано в следующем коде:
IEnumerable<T> collection = new T[5];
foreach (V item in collection) { }
В предыдущей форме тип T элемента коллекции должен быть неявно или явно преобразован в тип V переменной итерации. Если явное преобразование из T в V завершается ошибкой во время выполнения, оператор foreach выдает исключение InvalidCastException. Например, если T является незапечатанным типом класса, V может быть любым типом интерфейса, даже тем, который T не реализует. Во время выполнения тип элемента коллекции может быть производным от T и фактически реализовать V. В противном случае возникает InvalidCastException.
Инструкция do
Оператор do выполняет оператор или блок операторов, пока определенное логическое выражение равно значению true. Так как это выражение оценивается после каждого выполнения цикла, цикл do выполняется один или несколько раз. Это отличает его от цикла while, который выполняется от нуля до нескольких раз.
В следующем примере показано применение оператора do.
int n = 0;
do
{
Console.Write(n);
n++;
} while (n < 5);
// Output:
// 01234
Инструкция while
Оператор while выполняет оператор или блок операторов, пока определенное логическое выражение равно значению true. Так как это выражение оценивается перед каждым выполнением цикла, цикл while выполняется ноль или несколько раз. Это отличает его от цикла do, который выполняется от одного до нескольких раз.
В следующем примере показано применение оператора while.
int n = 0;
while (n < 5)
{
Console.Write(n);
n++;
}
// Output:
// 01234
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:
- Оператор
for - Оператор
foreach - Оператор
do - Оператор
while
Дополнительные сведения о функциях, добавленных в C# 8.0 и более поздние версии, см. в следующих заметках о функциях.
- Асинхронные потоки (C# 8.0)
- Поддержка расширения
GetEnumeratorдля цикловforeach(C# 9.0)
См. также
- справочник по C#
- Использование оператора foreach с массивами
- Итераторы
