Перейти к содержимому
Теория · Массивы · урок 3 из 9

Цикл foreach

В прошлом уроке цикл for обходил массив по индексам. Часто индекс не нужен: надо только посмотреть, что лежит в каждом элементе. А напечатать массив одним вызовом Console.WriteLine не получится:

C#
int[] scores = [90, 75, 88];
Console.WriteLine(scores); // System.Int32[]
foreach (int score in scores)
{
    Console.WriteLine(score);
}
// 90
// 75
// 88

Console.WriteLine(scores) печатает не элементы, а название типа: System.Int32[] — так .NET называет массив int. Чтобы увидеть содержимое, массив обходят элемент за элементом.

Вспомните ряд шкафчиков. Цикл foreach — это проход вдоль ряда от первого шкафчика до последнего: у каждой дверцы вы заглядываете внутрь и делаете с тем, что там лежит, одно и то же. Номера шкафчиков при этом не нужны.

Как устроен foreach

Заголовок foreach (int score in scores) читается так: «для каждого score из scores». В нём три части:

  • int score — переменная цикла: на каждой итерации в ней лежит копия очередного элемента;
  • in — ключевое слово между переменной и массивом;
  • scores — массив, который обходит цикл.

Тело выполняется по одному разу для каждого элемента, по порядку от индекса 0 до последнего. В массиве из трёх элементов три итерации. За границу массива foreach не выходит: где кончается ряд, он знает сам.

Сумма и наибольший элемент

Многие задачи с массивами решают обходом по элементам:

  • посчитать сумму;
  • найти наибольшее значение;
  • сосчитать подходящие элементы.
C#
int[] temps = [-3, -1, -7, -4];
int sum = 0;
int max = temps[0];
foreach (int t in temps)
{
    sum += t;
    if (t > max)
    {
        max = t;
    }
}
Console.WriteLine(sum); // -15
Console.WriteLine(max); // -1

Давайте разберём, что делает программа:

  1. Переменная sum начинает с нуля, и на каждой итерации к ней прибавляется очередная температура.
  2. max начинает с первого элемента, temps[0].
  3. Если очередной элемент больше max, он становится новым максимумом.
  4. После цикла в sum лежит -15, а в max — -1.

Почему max начинает с temps[0], а не с нуля? Все температуры здесь отрицательные. Если начать с нуля, ни один элемент не окажется больше и программа напечатает 0 — число, которого в массиве нет. Эту ловушку часто проверяют на собеседовании в задаче о поиске максимума.

foreach или for

foreach выбирают, когда нужны только значения элементов. Цикл for нужен в двух случаях:

  • нужен сам индекс: напечатать номер элемента или сравнить соседей a[i] и a[i + 1];
  • нужно записать в элементы новые значения.

Во втором случае без for не обойтись: переменная цикла foreach доступна только для чтения, поэтому менять элементы приходится по индексу:

C#
int[] scores = [90, 75, 88];
for (int i = 0; i < scores.Length; i++)
{
    scores[i] = scores[i] + 5;
}
Console.WriteLine(string.Join(", ", scores)); // 95, 80, 93

string.Join(", ", scores) склеивает элементы массива в одну строку и ставит между ними первый аргумент, здесь запятую с пробелом. Так массив удобно напечатать целиком.

Здесь картинка со шкафчиками перестаёт работать. Стоя у открытой дверцы, вещь в шкафчике можно заменить. Переменная foreach — копия того, что лежит в элементе, а не открытая дверца: через неё содержимое не заменить.

Частая ошибка

Элементы попытались изменить через переменную foreach:

C#
int[] scores = [90, 75, 88];
foreach (int score in scores)
{
    score = score + 5; // ✗
}
TEXT
error CS1656: Cannot assign to 'score' because it is a 'foreach iteration variable'

Программа не компилируется: переменная цикла foreach доступна только для чтения. Чтобы изменить элементы, обходите массив циклом for по индексам, как в примере выше.

Главное

  • foreach (int score in scores) проходит по всем элементам массива по порядку, без индексов и без выхода за границу.
  • Переменная цикла foreach получает копию элемента и доступна только для чтения.
  • Чтобы изменить элементы или узнать индекс, нужен цикл for.
  • Console.WriteLine(scores) печатает тип System.Int32[], а string.Join(", ", scores) — элементы через запятую.