Программа курса← C#: Базовый курс
1Знакомство с C# и .NET
2Данные и типы
3Ветвления и циклы
4Методы и ошибки
5Коллекции и строки
6Классы и объекты

Циклы for, while и do-while

Цикл повторяет кусок кода. Видов в 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.