Skip to Content
Week 01Day 3 - 控制流

Day 3 - 控制流

建议用时:180-210 分钟

你将学会什么

  • 程序为什么需要条件判断和循环
  • if / else if / else 的执行顺序
  • 比较运算符和逻辑运算符怎么读
  • switch 适合处理什么问题
  • forwhileforeach 分别适合什么场景
  • breakcontinuereturn 的区别
  • 如何写一个带输入校验的成绩判断程序
  • 如何写一个可以反复选择的菜单程序

本页只讲 C# 控制流。今天的目标是让程序不再只会从上到下一直执行,而是能根据输入选择路线,也能重复处理一组动作。

本页固定顺序

  1. 先学第一部分:弄懂今天最小、最重要的知识,并运行短例子。
  2. 再学第二部分:把刚学的知识组合成一个完整例子。
  3. 然后做第三部分:自己跟着敲,再完成重复训练和每日小测。
  4. 最后做第四部分:先独立完成作业,再用完整答案检查。

学习衔接

上一页学习的是“变量与类型”,今天继续学习“控制流”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。

今天的最低通过线

第一次学习不要求背完整页。完成下面 3 项,就可以继续:

  • 能用自己的话说明“控制流”解决什么问题。
  • 把第一部分的短例子亲手敲完,并确认每个例子都能运行。
  • 不看完整答案完成第三部分至少前 3 个例子,再主动改一个值观察结果。

第一部分:先学原理和最小知识

这一部分先解决“这是什么、为什么要这样写”。每看到一个短例子就亲手敲一遍并运行,不要先背完整程序。

今天先抓住什么

控制流解决两类问题:

选择:满足条件就走这条路,否则走另一条路 重复:同一类动作要做多次

没有控制流,程序只能一行一行执行,无法处理错误输入、等级判断、菜单选择、批量数据。

名词先讲清楚

名词直接解释例子
条件最终结果是 truefalse 的判断score >= 60
分支不同条件执行不同代码块if / else
循环重复执行一段代码forwhileforeach
代码块{ } 包起来的一组代码if 后面的 { }
比较运算符比较两个值>>=<<===!=
逻辑运算符合并多个条件&&||!
break结束整个循环菜单选择退出
continue跳过本轮,进入下一轮跳过错误数据
return结束当前程序或方法输入错误后提前结束

程序默认只会从上往下执行

看这段代码:

Console.WriteLine("第一行"); Console.WriteLine("第二行"); Console.WriteLine("第三行");

输出一定是:

第一行 第二行 第三行

没有控制流时,程序不会自己选择,也不会自己重复。

if 只看真假

if 后面的条件最终必须能得到一个 bool,也就是 truefalse

int age = 20; bool canEnter = age >= 18; if (canEnter) { Console.WriteLine("可以进入"); } else { Console.WriteLine("不能进入"); }

age >= 18 会算出 true,所以进入第一个代码块。

比较运算符怎么读

写法读法例子结果
a > ba 大于 b10 > 3true
a >= ba 大于等于 b10 >= 10true
a < ba 小于 b3 < 10true
a <= ba 小于等于 b3 <= 2false
a == ba 等于 b3 == 3true
a != ba 不等于 b3 != 4true

最容易混的是 ===

int count = 3; // = 是赋值 Console.WriteLine(count == 3); // == 是比较

逻辑运算符怎么读

写法读法什么时候为 true
a && ba 并且 b两边都成立
a || ba 或者 b至少一边成立
!a不是 aa 为 false 时

例子:

int score = 75; bool isValid = score >= 0 && score <= 100; bool isExcellent = score >= 90; bool isInvalid = !isValid; Console.WriteLine(isValid); Console.WriteLine(isExcellent); Console.WriteLine(isInvalid);

输出:

True False False

else if 顺序为什么重要

程序会从上往下检查条件,遇到第一个成立的分支,就不会再检查后面的 else if

错误顺序:

int score = 95; if (score >= 60) { Console.WriteLine("通过"); } else if (score >= 90) { Console.WriteLine("优秀"); }

这段会输出:

通过

因为 score >= 60 已经成立了。更严格的条件要放前面。

正确顺序:

int score = 95; if (score >= 90) { Console.WriteLine("优秀"); } else if (score >= 60) { Console.WriteLine("通过"); }

switch 适合固定选项

如果判断的是几个固定选项,比如菜单选择 120switch 比一串 else if 更清楚。

Console.WriteLine("1. 添加"); Console.WriteLine("2. 查询"); Console.WriteLine("0. 退出"); Console.Write("请选择: "); string? choice = Console.ReadLine(); switch (choice) { case "1": Console.WriteLine("执行添加"); break; case "2": Console.WriteLine("执行查询"); break; case "0": Console.WriteLine("退出程序"); break; default: Console.WriteLine("未知选项"); break; }

范围判断用 if / else if,固定选项用 switch

四种循环怎么选

循环适合场景例子
for次数明确,常和数字下标有关打印 1 到 10
while次数不确定,满足条件就继续菜单反复显示
do while至少执行一次先显示菜单再判断是否继续
foreach遍历集合里的每一项输出所有成绩

控制流常用写法速查

控制流不是只会写 iffor,更重要的是知道常见问题应该套哪种写法。

需求常用写法例子
输入格式不对就停止if (!TryParse(...)) return;成绩必须是整数
数字范围校验if (score < 0 || score > 100)成绩必须 0 到 100
多个条件都满足if (nameOk && priceOk)名称和价格都合法
任意一个条件满足if (isAdmin || isOwner)管理员或本人可操作
固定选项菜单switch (choice)1 新增,0 退出
次数明确重复for打印 1 到 10
次数不确定重复while菜单反复显示
遍历集合foreach处理每个成绩
跳过本轮continue跳过错误数据
结束循环break菜单选择退出
累计数量count++统计通过人数
累计总和sum += value统计总分、总价

模板 1:输入校验后提前结束

Console.Write("数量: "); string? input = Console.ReadLine(); if (!int.TryParse(input, out int count)) { Console.WriteLine("数量必须是整数"); return; } if (count <= 0) { Console.WriteLine("数量必须大于 0"); return; } Console.WriteLine($"数量有效: {count}");

这个模板以后会反复出现:先挡住错误输入,再进入正常逻辑。

模板 2:遍历数据并统计

var scores = new List<int> { 95, 58, 76, 40, 88 }; int passedCount = 0; int totalScore = 0; foreach (int score in scores) { totalScore += score; if (score >= 60) { passedCount++; } } Console.WriteLine($"总分: {totalScore}"); Console.WriteLine($"通过人数: {passedCount}");

+= 表示在原来的基础上继续加。passedCount++ 表示数量加 1。

模板 3:菜单循环

while (true) { Console.WriteLine("1. 新增"); Console.WriteLine("2. 查询"); Console.WriteLine("0. 退出"); Console.Write("请选择: "); string? choice = Console.ReadLine(); if (choice == "0") { break; } Console.WriteLine($"你选择了: {choice}"); }

菜单循环一定要有退出条件,否则程序会一直重复。

for 循环拆开看

for (int i = 1; i <= 3; i++) { Console.WriteLine($"第 {i} 次"); }

这行可以拆成三部分:

部分意思
int i = 1从 1 开始
i <= 3只要 i 小于等于 3,就继续
i++每轮结束后 i 加 1

输出:

第 1 次 第 2 次 第 3 次

while 循环必须有出口

while (true) 表示一直循环,所以里面必须有 break

while (true) { Console.Write("输入 exit 退出: "); string? input = Console.ReadLine(); if (input == "exit") { break; } Console.WriteLine($"你输入了: {input}"); }

没有 break,这个循环就不会自己结束。

breakcontinuereturn 的区别

写法作用范围效果
break循环或 switch结束整个循环或当前 switch
continue循环跳过本轮,进入下一轮
return当前方法或程序直接结束当前流程

看例子:

for (int i = 1; i <= 5; i++) { if (i == 2) { continue; } if (i == 4) { break; } Console.WriteLine(i); }

输出:

1 3

2continue 跳过,循环到 4 时被 break 结束。

今天写代码时按这个顺序

  1. 先写正常输入会走的路线。
  2. 再写错误输入会走的路线。
  3. 范围判断先写最严格的条件。
  4. 固定菜单选项优先考虑 switch
  5. 需要重复时再加循环。
  6. 循环必须有结束条件。
  7. 最后用正常输入和错误输入各跑一次。

第二部分:把知识组合成完整例子

前面已经学过最小知识。现在把它们组合起来,先读懂执行顺序,再完整敲一遍。今天最终要写的程序会读取成绩,判断是否合法,再输出等级。

最终你要写出这个程序

Console.Write("请输入成绩: "); string? input = Console.ReadLine(); if (!int.TryParse(input, out int score)) { Console.WriteLine("成绩必须是整数"); return; } if (score < 0 || score > 100) { Console.WriteLine("成绩必须在 0 到 100 之间"); return; } string level; if (score >= 90) { level = "优秀"; } else if (score >= 80) { level = "良好"; } else if (score >= 60) { level = "通过"; } else { level = "未通过"; } Console.WriteLine($"成绩: {score}"); Console.WriteLine($"等级: {level}");

输入 95

请输入成绩: 95 成绩: 95 等级: 优秀

输入 abc

请输入成绩: abc 成绩必须是整数

输入 120

请输入成绩: 120 成绩必须在 0 到 100 之间

这个例子里,程序不再是简单向下执行,而是根据输入走不同路线。

第三部分:跟着敲代码

从这里开始动手。每个例子都可以直接放进 Program.cs 运行。

动手前先做这 3 件事

  1. 打开控制台项目,确认 dotnet run 能运行。
  2. 每做一个例子,就把 Program.cs 旧内容替换掉。
  3. 每次只改一个输入或一个条件,看输出怎么变。

例子 1:最简单的条件判断

int score = 78; if (score >= 60) { Console.WriteLine("通过"); } else { Console.WriteLine("未通过"); }

预期输出:

通过

score 改成 45,再运行一次,输出会变成“未通过”。

例子 2:成绩等级判断

int score = 95; if (score >= 90) { Console.WriteLine("A"); } else if (score >= 80) { Console.WriteLine("B"); } else if (score >= 60) { Console.WriteLine("C"); } else { Console.WriteLine("D"); }

预期输出:

A

score 分别改成 857040,观察走到哪个分支。

例子 3:输入成绩并校验范围

Console.Write("成绩: "); string? input = Console.ReadLine(); if (!int.TryParse(input, out int score)) { Console.WriteLine("成绩必须是整数"); return; } if (score < 0 || score > 100) { Console.WriteLine("成绩必须在 0 到 100 之间"); return; } if (score >= 60) { Console.WriteLine("通过"); } else { Console.WriteLine("未通过"); }

要分别测试:

输入 80:输出 通过 输入 40:输出 未通过 输入 abc:输出 成绩必须是整数 输入 120:输出 成绩必须在 0 到 100 之间

例子 4:for 循环打印 1 到 5

for (int i = 1; i <= 5; i++) { Console.WriteLine(i); }

预期输出:

1 2 3 4 5

例子 5:用 continue 跳过奇数

for (int i = 1; i <= 10; i++) { if (i % 2 != 0) { continue; } Console.WriteLine(i); }

预期输出:

2 4 6 8 10

% 是取余数。i % 2 != 0 表示不能被 2 整除,也就是奇数。

例子 6:foreach 遍历成绩列表

var scores = new List<int> { 95, 58, 76, 40, 88 }; int passedCount = 0; foreach (int score in scores) { if (score >= 60) { Console.WriteLine($"{score} 通过"); passedCount++; } else { Console.WriteLine($"{score} 未通过"); } } Console.WriteLine($"通过人数: {passedCount}");

这个例子把集合、循环、判断放在一起。passedCount++ 表示通过人数加 1。

例子 7:菜单循环

while (true) { Console.WriteLine("1. 打印当前时间"); Console.WriteLine("2. 打印欢迎语"); Console.WriteLine("0. 退出"); Console.Write("请选择: "); string? choice = Console.ReadLine(); if (choice == "1") { Console.WriteLine(DateTime.Now); } else if (choice == "2") { Console.WriteLine("欢迎学习 C#"); } else if (choice == "0") { Console.WriteLine("程序结束"); break; } else { Console.WriteLine("未知选项"); } }

这个例子要重点看:只要没有输入 0,程序就会回到菜单。

例子 8:用 switch 写菜单

Console.WriteLine("1. 添加商品"); Console.WriteLine("2. 查看商品"); Console.WriteLine("0. 退出"); Console.Write("请选择: "); string? choice = Console.ReadLine(); switch (choice) { case "1": Console.WriteLine("执行添加商品"); break; case "2": Console.WriteLine("执行查看商品"); break; case "0": Console.WriteLine("退出程序"); break; default: Console.WriteLine("未知选项"); break; }

固定选项较多时,switch 通常比很多个 else if 更清楚。

例子 9:跳过错误数据并统计总数

var scoreTexts = new List<string> { "95", "abc", "58", "120", "76" }; int validCount = 0; int invalidCount = 0; int totalScore = 0; foreach (string scoreText in scoreTexts) { if (!int.TryParse(scoreText, out int score)) { Console.WriteLine($"{scoreText} 不是整数,跳过"); invalidCount++; continue; } if (score < 0 || score > 100) { Console.WriteLine($"{score} 超出范围,跳过"); invalidCount++; continue; } validCount++; totalScore += score; Console.WriteLine($"{score} 已计入统计"); } Console.WriteLine($"有效数据: {validCount}"); Console.WriteLine($"错误数据: {invalidCount}"); Console.WriteLine($"有效总分: {totalScore}");

预期输出:

95 已计入统计 abc 不是整数,跳过 58 已计入统计 120 超出范围,跳过 76 已计入统计 有效数据: 3 错误数据: 2 有效总分: 229

这里的关键是 continue:遇到错误数据时跳过本轮,不影响后面的数据继续处理。

常见错误和修法

错误为什么错修法
= 当成判断= 是赋值,不是比较判断相等用 ==
if 后面条件不是布尔值C# 条件必须是 truefalse写成 score >= 60 这类表达式
多个条件顺序写反前面的条件会先匹配范围判断从更严格的条件开始
循环忘记改变变量while 可能无限循环每轮更新计数器或读取新输入
breakcontinue 混用一个结束循环,一个跳过本轮先写注释说明想结束还是跳过

每日小测

做完本页后,用这 5 题检查是否真的掌握。

1. 判断题

本页的目标不是只把代码运行起来,还要能说清楚“为什么这样写”。

答案:对。能运行只是第一步,能解释原理、常用操作和常见错误,才说明本页内容进入了可复用能力。

2. 填空题

本页主题是:控制流。今天至少要掌握的 3 个点是:

1. 程序为什么需要条件判断和循环 2. `if / else if / else` 的执行顺序 3. 比较运算符和逻辑运算符怎么读

答案:以上 3 点必须能用自己的代码跑通,不能只停留在阅读。

3. 流程题

遇到本页相关功能时,先按什么顺序处理?

答案:先看完整例子,确认最终效果;再读原理和名词;然后跟着第三部分从空项目敲代码;最后对照作业答案检查。

4. 找错误题

如果本页代码运行失败,第一步应该做什么?

答案:先看终端或 IDE 里的第一条错误,找到文件名和行号;不要同时改很多地方。再回到本页的“常见错误和修法”表格,对照错误类型逐项排查。

5. 改需求题

在本页完整例子跑通后,至少改一个小需求。

可选改法:

  • 改一个字段名称。
  • 多加一个校验条件。
  • 多输出一行结果。
  • 把固定数据改成用户输入。
  • 把一次处理改成多条数据处理。

答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:程序为什么需要条件判断和循环。

上位机专项练习

上位机需要判断是否报警、根据状态码显示文字,还要循环读取多个采集值。控制流就是这些功能的基础。

下面 3 个例子都要亲手敲。先运行原代码,再完成每个例子后面的改动任务。

专项例子 1:判断温度是否报警

double temperature = 82.5; double alarmLimit = 80.0; if (temperature >= alarmLimit) { Console.WriteLine("高温报警"); } else { Console.WriteLine("温度正常"); }

运行结果或界面效果:

高温报警

改动任务: 把 temperature 改成 79.9,再运行。

专项例子 2:把设备状态码翻译成文字

int statusCode = 2; string statusText = statusCode switch { 0 => "离线", 1 => "运行", 2 => "报警", _ => "未知" }; Console.WriteLine(statusText);

运行结果或界面效果:

报警

改动任务: 分别测试 0、1、9 三个状态码。

专项例子 3:循环显示三次采集值

double[] values = { 20.1, 20.5, 21.0 }; foreach (double value in values) { Console.WriteLine($"采集值: {value:F1}"); }

运行结果或界面效果:

采集值: 20.1 采集值: 20.5 采集值: 21.0

改动任务: 增加一个 22.3,再观察循环次数。

第四部分:作业完整答案

这一部分给出当天作业的完整答案。先按第三部分做一遍,再用这里检查。

作业要求

写一个成绩等级程序,要求:

  1. 反复显示菜单。
  2. 输入 1 时,录入一个成绩并输出等级。
  3. 输入 0 时,退出程序。
  4. 成绩必须是整数。
  5. 成绩必须在 0 到 100 之间。
  6. 等级规则:
    • 90-100:优秀
    • 80-89:良好
    • 60-79:通过
    • 0-59:未通过

作业拆解

按这个顺序写:

  1. 先写 while (true) 菜单循环。
  2. 在循环里读取菜单选择。
  3. 选择 0 时用 break 退出。
  4. 选择 1 时读取成绩。
  5. TryParse 校验整数。
  6. 用范围判断拦住小于 0 或大于 100。
  7. if / else if / else 判断等级。

完整代码

while (true) { Console.WriteLine("==== 成绩菜单 ===="); Console.WriteLine("1. 输入成绩并判断等级"); Console.WriteLine("0. 退出"); Console.Write("请选择: "); string? choice = Console.ReadLine(); if (choice == "0") { Console.WriteLine("程序结束"); break; } if (choice != "1") { Console.WriteLine("未知选项"); continue; } Console.Write("请输入成绩: "); string? input = Console.ReadLine(); if (!int.TryParse(input, out int score)) { Console.WriteLine("成绩必须是整数"); continue; } if (score < 0 || score > 100) { Console.WriteLine("成绩必须在 0 到 100 之间"); continue; } string level; if (score >= 90) { level = "优秀"; } else if (score >= 80) { level = "良好"; } else if (score >= 60) { level = "通过"; } else { level = "未通过"; } Console.WriteLine($"成绩: {score}"); Console.WriteLine($"等级: {level}"); }

正确运行结果

输入 1,再输入 95

==== 成绩菜单 ==== 1. 输入成绩并判断等级 0. 退出 请选择: 1 请输入成绩: 95 成绩: 95 等级: 优秀

输入 1,再输入 abc

请输入成绩: abc 成绩必须是整数

输入 0

程序结束

你要能说清楚这 7 件事

  • if 后面的条件最终必须是 truefalse
  • else if 会从上往下检查,顺序会影响结果。
  • && 表示两个条件都要成立。
  • || 表示至少一个条件成立。
  • for 适合次数明确的重复。
  • while 适合不知道要重复几次的流程。
  • continue 是跳过本轮,break 是结束循环,return 是结束当前流程。

加强作业:批量成绩统计

要求:

  1. 给定一组成绩文本。
  2. 能转成整数并且在 0 到 100 之间的成绩才参与统计。
  3. 错误格式和超出范围的数据要跳过。
  4. 输出有效数量、错误数量、通过人数、总分和平均分。

完整答案:

var scoreTexts = new List<string> { "95", "abc", "58", "120", "76", "-1", "88" }; int validCount = 0; int invalidCount = 0; int passedCount = 0; int totalScore = 0; foreach (string scoreText in scoreTexts) { if (!int.TryParse(scoreText, out int score)) { Console.WriteLine($"{scoreText} 不是整数,跳过"); invalidCount++; continue; } if (score < 0 || score > 100) { Console.WriteLine($"{score} 不在 0 到 100 之间,跳过"); invalidCount++; continue; } validCount++; totalScore += score; if (score >= 60) { passedCount++; } } if (validCount == 0) { Console.WriteLine("没有有效成绩"); return; } decimal average = (decimal)totalScore / validCount; Console.WriteLine($"有效数量: {validCount}"); Console.WriteLine($"错误数量: {invalidCount}"); Console.WriteLine($"通过人数: {passedCount}"); Console.WriteLine($"总分: {totalScore}"); Console.WriteLine($"平均分: {average:F2}");

正确输出:

abc 不是整数,跳过 120 不在 0 到 100 之间,跳过 -1 不在 0 到 100 之间,跳过 有效数量: 4 错误数量: 3 通过人数: 3 总分: 317 平均分: 79.25

这里用到的常用操作:

写法作用
foreach逐个处理成绩文本
int.TryParse安全转换整数
continue跳过错误数据
validCount++有效数量加 1
totalScore += score累加总分
(decimal)totalScore / validCount转成小数后再算平均分
{average:F2}平均分保留两位小数