Поделиться через


Операторы итерации — 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++
    
  • Тело цикла, которое должно быть операцией или блоком инструкций.

Раздел итератора может содержать ноль или более следующих выражений инструкций, разделенных запятыми:

Если вы не объявляете переменную цикла в разделе инициализатора, можно также использовать ноль или больше выражений из предыдущего списка в разделе инициализатора. В следующем примере показано несколько менее распространенных вариантов использования инициализатора и итератора: назначение значения внешней переменной в разделе инициализатора, вызов метода в разделах инициализатора и итератора и изменение значений двух переменных в разделе итератора:

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 тип элемента коллекции), можно объявить переменную итерации с refref 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#:

Дополнительные сведения об этих функциях см. в следующих заметках о предложении функций:

См. также