Menu

Циклы while и do while в C#: синтаксис, циклы ввода и ловушки

Как работают циклы while и do while в C#: условие проверяется до или после тела, чтение ввода до стоп-слова или до конца ввода, while (true) с break и ошибки с бесконечными циклами, на которых спотыкаются многие.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Цикл while повторяет тело, пока условие остаётся истинным. Используйте его, когда заранее неизвестно, сколько итераций понадобится: продолжать, пока баланс не достигнет цели, очередь не опустеет или не закончится ввод.

Вывод:

Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years

Условие проверяется перед каждой итерацией, включая первую. Если бы savings уже достигло цели, тело не выполнилось бы ни разу, и программа напечатала бы «after 0 years». Цикл зависит от того, что тело меняет что-то, что читает условие; здесь savings растёт каждый год, пока savings < goal не станет ложным.

do while: хотя бы один раз

Цикл do while переносит проверку в конец, поэтому тело всегда выполняется хотя бы один раз:

Вывод:

Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts

Первое значение code появляется только после того, как тело выполнилось один раз, и именно в таких случаях подходит do while. Две детали синтаксиса часто подводят:

  • Точка с запятой после while (code != 1) обязательна. Без неё возникает ошибка компиляции.
  • Переменные, используемые в условии, нужно объявлять до do. Переменная, объявленная внутри фигурных скобок, к моменту проверки условия уже вне области видимости, поэтому do { int code = ...; } while (code != 1); не компилируется.

Тот же цикл, записанный через while, требует либо продублировать первый вызов перед циклом, либо фиктивного начального значения, которое вынуждает первый проход. do while напрямую выражает идею «попробовать, а затем решить, пробовать ли снова».

Чтение ввода до стоп-слова

Чтение строк до стоп-слова или до конца ввода это хрестоматийный цикл while. Console.ReadLine() возвращает null, когда ввода больше нет, поэтому проверяйте оба случая:

Вывод:

skipping "abc": not a number
3 amounts, total 49.50

(line = Console.ReadLine()) != null присваивает следующую строку и проверяет её в одном выражении; скобки вокруг присваивания обязательны, потому что != связывает сильнее, чем =. Цикл останавливается на done, поэтому 99 после него не читается. decimal.TryParse отвергает abc без исключения, а continue переходит к следующей строке. Без проверки на null при достижении конца ввода TryParse вечно получал бы null, поскольку ReadLine продолжает возвращать null.

do while подходит для приёма «спросить и проверить», где приглашение должно появиться хотя бы один раз:

Вывод:

Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15

Приглашение находится внутри цикла, поэтому оно повторяется после каждого отклонённого ответа, а age объявлена до do, чтобы код после цикла мог её использовать. Когда за клавиатурой пользователь, каждый ответ появляется в консоли после приглашения; при перенаправленном вводе, как здесь, видно только то, что выводит сама программа.

while (true) с break

Иногда естественная точка выхода находится в середине тела, после части работы и до остального. Запишите цикл как while (true) и выходите через break:

Вывод:

processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue

Dequeue забирает следующее задание из начала очереди, а цикл выходит, как только видит стоп-маркер, поэтому последнее задание остаётся в очереди. while (true) не является признаком плохого кода, когда выход понятен и находится близко к началу тела. Он становится таким, когда по длинному телу разбросано несколько break; тогда проще следить за именованным условием типа bool или методом с return. Выход из вложенных циклов описан на странице о break и continue.

Циклы, которые никогда не заканчиваются

У непреднамеренного бесконечного цикла одна из трёх причин.

Тело никогда не меняет условие. Счётчик не увеличивается, или увеличивается не та переменная:

int i = 0;
while (i < 5)
{
    Console.WriteLine(i);   // i++ forgotten: prints 0 forever
}

Точка с запятой после условия. Точка с запятой это всё тело цикла, а блок под ней выполняется после цикла (который никогда не завершается):

while (retries < 3);        // warning CS0642: Possible mistaken empty statement
{
    retries++;
}

Условие, которое перескакивает цель. Проверка !=, когда переменная меняется шагами, может перепрыгнуть значение остановки. while (x != 10) x += 3; проходит 0, 3, 6, 9, 12 и пролетает мимо 10 (на 10 он попадает лишь случайно, после того как int переполнится и перейдёт через границу миллиарды шагов спустя). Когда шаг не равен 1, проверяйте через < или > вместо !=. То же относится к значениям double, которые редко попадают точно в цель: while (x != 1.0) x += 0.1; никогда не завершается.

Выбор между while, do while и for

СитуацияЦикл
Известное число итераций или индексfor
Каждый элемент коллекцииforeach
Повторять, пока выполняется условие, возможно ни разуwhile
Тело должно выполниться один раз, прежде чем условие обретёт смыслdo while
Проверка выхода в середине телаwhile (true) с break

Любой цикл for можно переписать как цикл while и наоборот; выбор определяется тем, какой из них размещает логику цикла там, где её ожидает читатель. Когда есть счётчик, проверка и шаг, for держит их в одной строке. Когда цикл управляется состоянием, которое меняется внутри тела, об этом говорит while.

Часто задаваемые вопросы

Чем while отличается от do while в C#?

Цикл while проверяет условие перед каждой итерацией, поэтому его тело может выполниться ноль раз. Цикл do { } while (condition); проверяет после каждой итерации, поэтому его тело всегда выполняется хотя бы один раз. Используйте do while, когда первый проход даёт значение, которое проверяет условие, например при чтении ввода и последующей проверке.

Какой синтаксис у цикла do while в C#?

do { body } while (condition);. Обратите внимание на точку с запятой после закрывающей скобки: она обязательна, и её отсутствие вызывает ошибку компиляции. Условие может использовать переменные, присвоенные в теле, но объявлять их нужно до do, потому что фигурные скобки тела образуют отдельную область видимости.

Как написать while (true) в C#?

while (true) { ... } крутится бесконечно, пока что-то внутри не выйдет из цикла через break, return или исключение. Это естественная форма, когда проверка выхода находится в середине тела, например при чтении строки и остановке на пустой строке.

Как читать ввод, пока пользователь не введёт слово или ввод не закончится, в C#?

Крутите цикл по Console.ReadLine(), который возвращает null в конце ввода: string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }. Присваивание внутри условия читает следующую строку и проверяет её за один шаг.

Почему мой цикл while в C# никогда не заканчивается?

Обычно тело не меняет то, что проверяет условие (забытый i++), или сразу после условия стоит точка с запятой: while (count < 10); это цикл с пустым телом, который крутится вечно. О втором случае компилятор предупреждает через CS0642, Possible mistaken empty statement.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ