Операторы итерации — for
, foreach
, do
и while
Операторы итерации многократно выполняют инструкцию или блок инструкций. Оператор 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>, как показано в следующем примере.
List<int> fibNumbers = new() { 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
. ЭтотGetEnumerator
метод может быть методом расширения типа. - тип возвращаемого значения метода
GetEnumerator
должен содержать открытое свойствоCurrent
и открытый методMoveNext
без параметров с типом возвращаемого значенияbool
.
В следующем примере показано использование оператора foreach
с экземпляром типа System.Span<T>, который не реализует интерфейс:
Span<int> numbers = [3, 14, 15, 92, 6];
foreach (int number in numbers)
{
Console.Write($"{number} ");
}
// Output:
// 3 14 15 92 6
Если свойство перечислителя 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
пуста, тело оператора foreach
не выполняется и пропускается. Если оператор foreach
применяется к null
, возникает исключение NullReferenceException.
await foreach
Инструкцию 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. Дополнительные сведения о контекстах синхронизации и захвате текущего контекста см. в статье Использование асинхронного шаблона, основанного на задачах. Дополнительные сведения об асинхронных потоках см. в руководстве по асинхронным потокам.
Тип переменной итерации
Можно использовать ключевое слово var
, чтобы компилятор мог определить тип переменной итерации в операторе foreach
, как показано в следующем коде:
foreach (var item in collection) { }
Примечание.
var
Тип может быть выведен компилятором в качестве ссылочного типа, допускающего значение NULL, в зависимости от того, включен ли контекст с поддержкой NULL и является ли тип выражения инициализации ссылочным типом.
Дополнительные сведения см . в разделе неявно типизированные локальные переменные.
Можно также явно указать тип переменной итерации, как показано в следующем коде:
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
выполняется один или несколько раз. Цикл do
отличается от while
цикла, который выполняется нулевым или более раз.
В следующем примере показано применение оператора do
.
int n = 0;
do
{
Console.Write(n);
n++;
} while (n < 5);
// Output:
// 01234
Инструкция while
Оператор while
выполняет оператор или блок операторов, пока определенное логическое выражение равно значению true
. Так как это выражение оценивается перед каждым выполнением цикла, цикл while
выполняется ноль или несколько раз. Цикл while
отличается от do
цикла, который выполняется один или несколько раз.
В следующем примере показано применение оператора while
.
int n = 0;
while (n < 5)
{
Console.Write(n);
n++;
}
// Output:
// 01234
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:
Дополнительные сведения об этих функциях см. в следующих заметках о предложении функций: