Цикл foreach
В прошлом уроке цикл for обходил массив по индексам. Часто индекс не нужен: надо только посмотреть, что лежит в каждом элементе. А напечатать массив одним вызовом Console.WriteLine не получится:
int[] scores = [90, 75, 88];
Console.WriteLine(scores); // System.Int32[]
foreach (int score in scores)
{
Console.WriteLine(score);
}
// 90
// 75
// 88Console.WriteLine(scores) печатает не элементы, а название типа: System.Int32[] — так .NET называет массив int. Чтобы увидеть содержимое, массив обходят элемент за элементом.
Вспомните ряд шкафчиков. Цикл foreach — это проход вдоль ряда от первого шкафчика до последнего: у каждой дверцы вы заглядываете внутрь и делаете с тем, что там лежит, одно и то же. Номера шкафчиков при этом не нужны.
Как устроен foreach
Заголовок foreach (int score in scores) читается так: «для каждого score из scores». В нём три части:
int score— переменная цикла: на каждой итерации в ней лежит копия очередного элемента;in— ключевое слово между переменной и массивом;scores— массив, который обходит цикл.
Тело выполняется по одному разу для каждого элемента, по порядку от индекса 0 до последнего. В массиве из трёх элементов три итерации. За границу массива foreach не выходит: где кончается ряд, он знает сам.
Сумма и наибольший элемент
Многие задачи с массивами решают обходом по элементам:
- посчитать сумму;
- найти наибольшее значение;
- сосчитать подходящие элементы.
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Давайте разберём, что делает программа:
- Переменная
sumначинает с нуля, и на каждой итерации к ней прибавляется очередная температура. maxначинает с первого элемента,temps[0].- Если очередной элемент больше
max, он становится новым максимумом. - После цикла в
sumлежит-15, а вmax—-1.
Почему max начинает с temps[0], а не с нуля? Все температуры здесь отрицательные. Если начать с нуля, ни один элемент не окажется больше и программа напечатает 0 — число, которого в массиве нет. Эту ловушку часто проверяют на собеседовании в задаче о поиске максимума.
foreach или for
foreach выбирают, когда нужны только значения элементов. Цикл for нужен в двух случаях:
- нужен сам индекс: напечатать номер элемента или сравнить соседей
a[i]иa[i + 1]; - нужно записать в элементы новые значения.
Во втором случае без for не обойтись: переменная цикла foreach доступна только для чтения, поэтому менять элементы приходится по индексу:
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, 93string.Join(", ", scores) склеивает элементы массива в одну строку и ставит между ними первый аргумент, здесь запятую с пробелом. Так массив удобно напечатать целиком.
Здесь картинка со шкафчиками перестаёт работать. Стоя у открытой дверцы, вещь в шкафчике можно заменить. Переменная foreach — копия того, что лежит в элементе, а не открытая дверца: через неё содержимое не заменить.
Частая ошибка
Элементы попытались изменить через переменную foreach:
int[] scores = [90, 75, 88];
foreach (int score in scores)
{
score = score + 5; // ✗
}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)— элементы через запятую.