Menu

C# while 与 do while 循环:语法、输入循环与常见陷阱

C# 中 while 和 do-while 循环如何工作:条件在循环体之前还是之后检查,读取输入直到遇到哨兵值或输入结束,用 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 变为 false。

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 检查,读到输入末尾之后,程序会永远把 null 传给 TryParse,因为 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(只有在 int 溢出并回绕、几十亿步之后,才会碰巧落在 10 上)。只要步长不是 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 就能清楚地表达这一点。

常见问题

C# 中 while 和 do while 有什么区别?

while 循环在每次迭代之前检查条件,所以循环体可能一次都不执行。do { } while (condition); 循环在每次迭代之后检查条件,所以循环体至少会执行一次。当第一遍执行才产生条件要检查的值时(比如先读取输入再验证),就用 do while。

C# 中 do while 循环的语法是什么?

do { body } while (condition);。注意右括号后面的分号:它是必需的,漏掉会导致编译错误。条件可以使用在循环体中赋值的变量,但这些变量必须在 do 之前声明,因为循环体的大括号是一个独立的作用域。

C# 中 while (true) 怎么写?

while (true) { ... } 会一直循环,直到循环体内部通过 break、return 或异常离开。当退出判断应该放在循环体中间时,这是最自然的写法,比如读取一行,遇到空行就停止。

在 C# 中如何读取输入,直到用户输入某个词或输入结束?

对 Console.ReadLine() 进行循环,它在输入结束时返回 null:string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }。条件里的赋值一步完成读取下一行并检查它。

为什么我的 C# while 循环一直不结束?

通常是循环体从来没有改变条件所检查的东西(忘了写 i++),或者条件后面紧跟了一个分号:while (count < 10); 是一个循环体为空的循环,会永远空转。对于第二种情况,编译器会给出警告 CS0642,Possible mistaken empty statement。

Coddy 编程语言插图

和 Coddy 一起学编程

开始