Цикл повторяет кусок кода. Видов в C# четыре, и выбор между ними зависит от того, что вы знаете заранее: сколько раз повторять или при каком условии остановиться.
for: когда количество известно
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i); // 0 1 2 3 4
}
В скобках три части, разделённые точками с запятой:
| Часть | Когда выполняется |
|---|---|
int i = 0 |
один раз перед началом |
i < 5 |
перед каждым проходом; ложно – цикл заканчивается |
i++ |
после каждого прохода |
Переменная i существует только внутри цикла. После закрывающей скобки её нет – обратиться к ней
не выйдет.
Счёт с нуля – не прихоть. Элементы массивов и строк нумеруются с нуля, поэтому цикл
for (int i = 0; i < n; i++) даёт ровно те индексы, которые существуют. Запись i < n при этом
означает «n повторений», и это самая частая форма записи в любом коде.
Классическая ошибка – i <= n при обходе массива: последний индекс равен n-1, и обращение по n даёт IndexOutOfRangeException. Ошибка настолько распространённая, что у неё есть имя: «ошибка на единицу».
Шаг может быть любым:
for (int i = 10; i > 0; i--) // обратный отсчёт
for (int i = 0; i < 100; i += 5) // через пять
while: когда условие важнее счёта
int attempts = 0;
while (attempts < 3)
{
Console.Write("Пароль: ");
string input = Console.ReadLine();
if (input == "secret")
{
Console.WriteLine("Верно");
break;
}
attempts++;
}
Условие проверяется перед каждым проходом. Если оно ложно с самого начала, тело не выполнится ни разу.
while берут, когда количество повторений заранее неизвестно: читать файл, пока не кончился;
спрашивать, пока не ответят правильно; ждать, пока не придёт ответ.
Внутри while обязано быть что-то, что рано или поздно сделает условие ложным. Забыли attempts++ – получили бесконечный цикл: программа не падает, а просто перестаёт отвечать. Прерывается это Ctrl+C.
do-while: хотя бы один раз
int number;
do
{
Console.Write("Введите число больше нуля: ");
}
while (!int.TryParse(Console.ReadLine(), out number) || number <= 0);
Условие проверяется после тела, поэтому один проход происходит всегда. Это ровно то, что нужно для запроса ввода: спросить надо в любом случае, а повторять – только если ответ не подошёл.
Обратите внимание на точку с запятой после while – в этой форме она обязательна.
Вложенные циклы
Цикл внутри цикла нужен для таблиц, матриц и всего двумерного:
for (int row = 1; row <= 3; row++)
{
for (int col = 1; col <= 3; col++)
{
Console.Write($"{row * col,4}");
}
Console.WriteLine();
}
Запись {row * col,4} выравнивает число по правому краю в поле шириной четыре символа – без
этого таблица умножения расползётся.
Внутренний цикл полностью проходит все свои повторения на каждом шаге внешнего. Если оба идут до тысячи, тело выполнится миллион раз – рост квадратичный, и об этом стоит помнить, когда данных много.
Не называйте счётчики вложенных циклов i и j, если можно назвать осмысленно. row и col сразу говорят, кто из них главный, а i и j легко перепутать местами – ошибку потом ищут глазами по всему телу цикла.
Бесконечный цикл – иногда это норма
Иногда выход из цикла естественнее описать изнутри, чем условием сверху:
while (true)
{
Console.Write("Команда: ");
string command = Console.ReadLine();
if (command == "выход")
{
break;
}
Console.WriteLine($"Не знаю команду «{command}»");
}
Так пишут главные циклы программ: меню, обработчики команд, серверы. Единственное требование –
выход должен быть виден. while (true) без единого break внутри – это ошибка.
Какой цикл выбрать
Правило почти всегда срабатывает:
| Знаете заранее | Берите |
|---|---|
| сколько раз повторять | for |
| условие, но не количество | while |
| что тело нужно выполнить хотя бы раз | do-while |
| что нужно обойти коллекцию целиком | foreach – о нём следующий урок |
Если сомневаетесь между for и while, посмотрите на счётчик: он нужен внутри тела? Тогда
for. Он существует только чтобы считать повторения, а вы обходите коллекцию? Значит, вам нужен
foreach.