4.1 为什么需要流程控制?

4.1.1 现实中到处都是"选择"

text

早上起床:
如果(今天周末){
    继续睡觉
}否则{
    起床去上班
}

过马路:
如果(绿灯亮){
    通过马路
}否则{
    等待
}

4.1.2 没有流程控制的程序

csharp

// 这样的程序永远只会做同一件事
Console.WriteLine("你好");
Console.WriteLine("你好");
Console.WriteLine("你好");
// 无法根据用户输入做出不同反应

4.1.3 有流程控制的程序

csharp

Console.Write("请输入你的年龄:");
int age = Convert.ToInt32(Console.ReadLine());

if (age >= 18)
{
    Console.WriteLine("欢迎进入网站!");
}
else
{
    Console.WriteLine("对不起,你未成年,不能访问。");
}

4.1.4 流程控制三大结构

text

结构化程序设计三大结构
├── 顺序结构(默认) → 代码从上往下依次执行
├── 选择结构 → if、switch,根据条件选择执行哪段代码
└── 循环结构 → for、while、do-while,重复执行某段代码

4.2 条件判断:if 语句

4.2.1 if 的基本语法

csharp

// 语法
if (条件表达式)
{
    // 条件为 true 时执行的代码
}

流程图

text

       开始
         │
         ▼
    ┌─────────┐
    │ 条件判断 │
    └────┬────┘
         │
     ┌───┴───┐
     │       │
    true    false
     │       │
     ▼       │
┌─────────┐  │
│ 执行代码 │  │
└────┬────┘  │
     │       │
     └───┬───┘
         │
         ▼
       结束

示例

csharp

int score = 85;

if (score >= 60)
{
    Console.WriteLine("恭喜,考试通过了!");
}
// 输出:恭喜,考试通过了!

4.2.2 if-else 语句

csharp

// 语法
if (条件表达式)
{
    // 条件为 true 时执行
}
else
{
    // 条件为 false 时执行
}

流程图

text

       开始
         │
         ▼
    ┌─────────┐
    │ 条件判断 │
    └────┬────┘
         │
     ┌───┴───┐
     │       │
    true    false
     │       │
     ▼       ▼
┌─────────┐ ┌─────────┐
│ 执行代码A│ │ 执行代码B│
└────┬────┘ └────┬────┘
     │           │
     └─────┬─────┘
           │
           ▼
         结束

示例

csharp

int temperature = 35;

if (temperature > 30)
{
    Console.WriteLine("天气太热了,开空调吧!");
}
else
{
    Console.WriteLine("天气不错,出去走走!");
}
// 输出:天气太热了,开空调吧!

4.2.3 if-else if-else 多条件判断

csharp

// 语法
if (条件1)
{
    // 条件1为 true 时执行
}
else if (条件2)
{
    // 条件1为 false 且条件2为 true 时执行
}
else if (条件3)
{
    // 条件1、2为 false 且条件3为 true 时执行
}
else
{
    // 所有条件都为 false 时执行
}

示例:成绩评级

csharp

Console.Write("请输入你的分数(0-100):");
int score = Convert.ToInt32(Console.ReadLine());

if (score >= 90)
{
    Console.WriteLine("评级:A(优秀)");
}
else if (score >= 80)
{
    Console.WriteLine("评级:B(良好)");
}
else if (score >= 70)
{
    Console.WriteLine("评级:C(中等)");
}
else if (score >= 60)
{
    Console.WriteLine("评级:D(及格)");
}
else
{
    Console.WriteLine("评级:F(不及格)");
}

4.2.4 嵌套 if

if 语句里面再放 if 语句

csharp

int age = 25;
bool hasID = true;

if (age >= 18)
{
    if (hasID)
    {
        Console.WriteLine("可以进入酒吧");
    }
    else
    {
        Console.WriteLine("请出示身份证");
    }
}
else
{
    Console.WriteLine("未成年人禁止入内");
}

// 可以用逻辑运算符简化
if (age >= 18 && hasID)
{
    Console.WriteLine("可以进入酒吧");
}
else if (age >= 18 && !hasID)
{
    Console.WriteLine("请出示身份证");
}
else
{
    Console.WriteLine("未成年人禁止入内");
}

4.2.5 常见陷阱:悬挂 else

csharp

// 错误示例:else 与哪个 if 匹配?
int x = 5;
if (x > 0)
    if (x > 10)
        Console.WriteLine("x大于10");
else
    Console.WriteLine("x不大于0");  // 这个 else 匹配的是内层 if!

// 正确做法:使用大括号明确范围
if (x > 0)
{
    if (x > 10)
    {
        Console.WriteLine("x大于10");
    }
}
else
{
    Console.WriteLine("x不大于0");
}

4.3 多路分支:switch 语句

4.3.1 什么时候用 switch?

当一个变量有很多种可能的取值,每种取值执行不同代码时,用 if-else 会很冗长,这时用 switch 更清晰。

if-else 版本(冗长)

csharp

int day = 3;

if (day == 1)
{
    Console.WriteLine("星期一");
}
else if (day == 2)
{
    Console.WriteLine("星期二");
}
else if (day == 3)
{
    Console.WriteLine("星期三");
}
// ... 需要写7个判断

switch 版本(清晰)

csharp

int day = 3;

switch (day)
{
    case 1:
        Console.WriteLine("星期一");
        break;
    case 2:
        Console.WriteLine("星期二");
        break;
    case 3:
        Console.WriteLine("星期三");
        break;
    case 4:
        Console.WriteLine("星期四");
        break;
    case 5:
        Console.WriteLine("星期五");
        break;
    case 6:
        Console.WriteLine("星期六");
        break;
    case 7:
        Console.WriteLine("星期日");
        break;
    default:
        Console.WriteLine("无效的日期");
        break;
}

4.3.2 switch 语法详解

csharp

switch (表达式)  // 表达式可以是 int、char、string、枚举等
{
    case 值1:     // 如果表达式的值等于值1
        // 执行的代码
        break;    // 跳出 switch(必须要有,除非特殊情况)
    
    case 值2:
        // 执行的代码
        break;
    
    case 值3:
    case 值4:     // 多个 case 共享同一段代码
        // 值3或值4都会执行这里
        break;
    
    default:      // 可选,没有任何 case 匹配时执行
        // 代码
        break;
}

4.3.3 完整示例

csharp

using System;

Console.WriteLine("=== 菜单选择 ===");
Console.WriteLine("1. 查看余额");
Console.WriteLine("2. 存款");
Console.WriteLine("3. 取款");
Console.WriteLine("4. 退出");
Console.Write("请选择(1-4):");

string choice = Console.ReadLine();

switch (choice)
{
    case "1":
        Console.WriteLine("您的余额是:1000元");
        break;
    case "2":
        Console.Write("请输入存款金额:");
        double amount = Convert.ToDouble(Console.ReadLine());
        Console.WriteLine($"成功存款{amount}元");
        break;
    case "3":
        Console.Write("请输入取款金额:");
        double withdraw = Convert.ToDouble(Console.ReadLine());
        Console.WriteLine($"成功取款{withdraw}元");
        break;
    case "4":
        Console.WriteLine("感谢使用,再见!");
        break;
    default:
        Console.WriteLine("无效选择,请重新输入!");
        break;
}

4.3.4 switch 新语法(C# 8.0+,开关表达式)

csharp

// 传统 switch 语句
string GetGrade(int score)
{
    switch (score)
    {
        case >= 90:
            return "A";
        case >= 80:
            return "B";
        case >= 70:
            return "C";
        case >= 60:
            return "D";
        default:
            return "F";
    }
}

// 新语法:switch 表达式(更简洁)
string GetGrade2(int score) => score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    >= 60 => "D",
    _ => "F"  // _ 相当于 default
};

4.4 循环结构

4.4.1 为什么需要循环?

没有循环

csharp

Console.WriteLine("第1次说:我热爱学习");
Console.WriteLine("第2次说:我热爱学习");
Console.WriteLine("第3次说:我热爱学习");
// ... 如果要写100次,代码会非常冗长

有循环

csharp

for (int i = 1; i <= 100; i++)
{
    Console.WriteLine($"第{i}次说:我热爱学习");
}

4.4.2 for 循环(最常用)

语法

csharp

for (初始化; 条件判断; 循环变量变化)
{
    // 循环体:条件为 true 时重复执行的代码
}

执行流程

text

第1步:执行"初始化"(只执行一次)
第2步:执行"条件判断"
第3步:如果为 true → 执行循环体 → 执行"循环变量变化" → 回到第2步
第4步:如果为 false → 退出循环

流程图

text

       开始
         │
         ▼
    ┌─────────┐
    │ 初始化   │
    └────┬────┘
         │
         ▼
    ┌─────────┐
    │ 条件判断 │◄────────┐
    └────┬────┘         │
         │              │
     ┌───┴───┐          │
     │       │          │
    true    false       │
     │       │          │
     ▼       │          │
┌─────────┐  │          │
│ 循环体   │  │          │
└────┬────┘  │          │
     │       │          │
     ▼       │          │
┌─────────┐  │          │
│ 循环变量 │  │          │
│ 变化    │──┘          │
└─────────┘             │
                        │
                        ▼
                      结束

示例1:输出 1 到 10

csharp

for (int i = 1; i <= 10; i++)
{
    Console.WriteLine(i);
}
// 输出:1 2 3 4 5 6 7 8 9 10

示例2:倒序输出 10 到 1

csharp

for (int i = 10; i >= 1; i--)
{
    Console.WriteLine(i);
}
// 输出:10 9 8 7 6 5 4 3 2 1

示例3:步长不为1

csharp

// 输出偶数
for (int i = 2; i <= 10; i += 2)
{
    Console.WriteLine(i);
}
// 输出:2 4 6 8 10

// 输出奇数
for (int i = 1; i <= 10; i += 2)
{
    Console.WriteLine(i);
}
// 输出:1 3 5 7 9

示例4:累加求和

csharp

int sum = 0;
for (int i = 1; i <= 100; i++)
{
    sum = sum + i;  // 或 sum += i
}
Console.WriteLine($"1到100的和是:{sum}");  // 5050

示例5:阶乘计算

csharp

Console.Write("请输入一个正整数:");
int n = Convert.ToInt32(Console.ReadLine());

int factorial = 1;
for (int i = 1; i <= n; i++)
{
    factorial *= i;  // factorial = factorial * i
}
Console.WriteLine($"{n}! = {factorial}");

4.4.3 while 循环

当不知道循环次数,只知道"满足某个条件时继续循环"时使用

语法

csharp

while (条件表达式)
{
    // 条件为 true 时重复执行
}

示例1:猜数字游戏

csharp

Random random = new Random();
int secretNumber = random.Next(1, 101);  // 1-100 随机数
int guess = 0;
int attempts = 0;

Console.WriteLine("猜猜我心里想的数字(1-100)");

while (guess != secretNumber)
{
    Console.Write("请输入你的猜测:");
    guess = Convert.ToInt32(Console.ReadLine());
    attempts++;

    if (guess > secretNumber)
    {
        Console.WriteLine("太大了,再试试!");
    }
    else if (guess < secretNumber)
    {
        Console.WriteLine("太小了,再试试!");
    }
}

Console.WriteLine($"恭喜!你猜了{attempts}次猜中了!");

示例2:输入验证

csharp

int age = -1;
while (age < 0 || age > 150)
{
    Console.Write("请输入年龄(0-150):");
    age = Convert.ToInt32(Console.ReadLine());
    
    if (age < 0 || age > 150)
    {
        Console.WriteLine("年龄无效,请重新输入!");
    }
}
Console.WriteLine($"您的年龄是:{age}");

4.4.4 do-while 循环

至少执行一次循环体,然后再判断条件

语法

csharp

do
{
    // 循环体(至少执行一次)
} while (条件表达式);

while vs do-while

csharp

// while:可能一次都不执行
int x = 10;
while (x < 5)
{
    Console.WriteLine("while 循环");  // 不会执行
}

// do-while:至少执行一次
int y = 10;
do
{
    Console.WriteLine("do-while 循环");  // 会执行一次
} while (y < 5);

实际应用:菜单循环

csharp

string choice;

do
{
    Console.Clear();  // 清屏
    Console.WriteLine("=== 主菜单 ===");
    Console.WriteLine("1. 功能A");
    Console.WriteLine("2. 功能B");
    Console.WriteLine("3. 退出");
    Console.Write("请选择:");
    
    choice = Console.ReadLine();
    
    switch (choice)
    {
        case "1":
            Console.WriteLine("执行功能A...");
            Console.WriteLine("按任意键返回菜单");
            Console.ReadKey();
            break;
        case "2":
            Console.WriteLine("执行功能B...");
            Console.WriteLine("按任意键返回菜单");
            Console.ReadKey();
            break;
        case "3":
            Console.WriteLine("谢谢使用,再见!");
            break;
        default:
            Console.WriteLine("无效选择,请重新输入!");
            Console.WriteLine("按任意键继续");
            Console.ReadKey();
            break;
    }
} while (choice != "3");

4.4.5 foreach 循环

专门用于遍历集合(数组、列表等),第五章会详细讲数组

csharp

// 预览
string[] names = { "张三", "李四", "王五" };

foreach (string name in names)
{
    Console.WriteLine(name);
}
// 输出:张三 李四 王五

4.5 跳转语句

4.5.1 break——跳出循环

csharp

// 查找第一个能被7整除的数
for (int i = 1; i <= 100; i++)
{
    if (i % 7 == 0)
    {
        Console.WriteLine($"第一个能被7整除的数是:{i}");
        break;  // 找到后立即退出循环
    }
}

4.5.2 continue——跳过本次循环

csharp

// 输出1到10中所有的奇数(跳过偶数)
for (int i = 1; i <= 10; i++)
{
    if (i % 2 == 0)
    {
        continue;  // 偶数跳过,不执行下面的代码
    }
    Console.WriteLine(i);
}
// 输出:1 3 5 7 9

4.5.3 break vs continue 对比

语句 作用 类比
break 立即终止整个循环 赛跑跑到终点,不跑了
continue 跳过本次,继续下一次循环 跳过路上的一个坑,继续跑

csharp

// 对比示例
Console.WriteLine("=== break 示例 ===");
for (int i = 1; i <= 5; i++)
{
    if (i == 3) break;
    Console.WriteLine(i);
}
// 输出:1 2

Console.WriteLine("=== continue 示例 ===");
for (int i = 1; i <= 5; i++)
{
    if (i == 3) continue;
    Console.WriteLine(i);
}
// 输出:1 2 4 5

4.5.4 goto——跳转到标签(不推荐)

goto 会让代码难以阅读,一般不建议使用,但了解即可

csharp

// 不推荐使用,仅作了解
for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 10; j++)
    {
        if (i == 5 && j == 5)
        {
            goto EndLoop;  // 跳出多层循环
        }
    }
}
EndLoop:
Console.WriteLine("跳出了多层循环");

4.6 循环嵌套

循环里面再放循环

示例1:打印矩形

csharp

int rows = 5;
int cols = 10;

for (int i = 0; i < rows; i++)      // 外层循环控制行数
{
    for (int j = 0; j < cols; j++)  // 内层循环控制每行列数
    {
        Console.Write("*");
    }
    Console.WriteLine();  // 每行结束后换行
}
// 输出:
// **********
// **********
// **********
// **********
// **********

示例2:打印三角形

csharp

int height = 5;

for (int i = 1; i <= height; i++)  // 行数
{
    for (int j = 1; j <= i; j++)   // 每行的星号数 = 行号
    {
        Console.Write("*");
    }
    Console.WriteLine();
}
// 输出:
// *
// **
// ***
// ****
// *****

示例3:打印九九乘法表

csharp

for (int i = 1; i <= 9; i++)
{
    for (int j = 1; j <= i; j++)
    {
        Console.Write($"{j}×{i}={i * j}\t");
    }
    Console.WriteLine();
}
// 输出:
// 1×1=1
// 1×2=2    2×2=4
// 1×3=3    2×3=6    3×3=9
// ...

4.7 综合示例

示例1:猜数字游戏(完整版)

csharp

using System;

Console.WriteLine("=== 猜数字游戏 ===");
Console.WriteLine("我已经想好了一个1-100之间的数字");
Console.WriteLine("你有10次机会猜中它!");

Random random = new Random();
int secretNumber = random.Next(1, 101);
int guess;
int attempts = 0;
int maxAttempts = 10;
bool guessedCorrectly = false;

while (attempts < maxAttempts && !guessedCorrectly)
{
    Console.Write($"\n还剩{maxAttempts - attempts}次机会,请输入你的猜测:");
    
    if (!int.TryParse(Console.ReadLine(), out guess))
    {
        Console.WriteLine("请输入有效的数字!");
        continue;
    }
    
    attempts++;
    
    if (guess == secretNumber)
    {
        guessedCorrectly = true;
        Console.WriteLine($"\n🎉 恭喜!你用了{attempts}次猜中了数字{secretNumber}!");
    }
    else if (guess < secretNumber)
    {
        Console.WriteLine("太小了,再试试更大的数字!");
    }
    else
    {
        Console.WriteLine("太大了,再试试更小的数字!");
    }
}

if (!guessedCorrectly)
{
    Console.WriteLine($"\n😭 很遗憾,机会用完了!正确答案是:{secretNumber}");
}

示例2:学生成绩统计系统

csharp

using System;

Console.WriteLine("=== 学生成绩统计系统 ===");
Console.Write("请输入班级人数:");
int studentCount = Convert.ToInt32(Console.ReadLine());

double sum = 0;
int maxScore = int.MinValue;
int minScore = int.MaxValue;
int passCount = 0;
int failCount = 0;

for (int i = 1; i <= studentCount; i++)
{
    Console.Write($"请输入第{i}个学生的成绩:");
    int score = Convert.ToInt32(Console.ReadLine());
    
    sum += score;
    
    if (score > maxScore) maxScore = score;
    if (score < minScore) minScore = score;
    
    if (score >= 60)
        passCount++;
    else
        failCount++;
}

double average = sum / studentCount;

Console.WriteLine("\n=== 统计结果 ===");
Console.WriteLine($"总人数:{studentCount}");
Console.WriteLine($"总分:{sum}");
Console.WriteLine($"平均分:{average:F2}");
Console.WriteLine($"最高分:{maxScore}");
Console.WriteLine($"最低分:{minScore}");
Console.WriteLine($"及格人数:{passCount},及格率:{(double)passCount / studentCount * 100:F1}%");
Console.WriteLine($"不及格人数:{failCount},不及格率:{(double)failCount / studentCount * 100:F1}%");

// 评级分布
int aCount = 0, bCount = 0, cCount = 0, dCount = 0, fCount = 0;
// 这里需要重新输入一遍成绩,或者用数组存储(第五章)

示例3:ATM 模拟系统

csharp

using System;

double balance = 1000;  // 初始余额
bool isRunning = true;

Console.WriteLine("=== ATM 模拟系统 ===");

while (isRunning)
{
    Console.WriteLine("\n请选择操作:");
    Console.WriteLine("1. 查询余额");
    Console.WriteLine("2. 存款");
    Console.WriteLine("3. 取款");
    Console.WriteLine("4. 退出");
    Console.Write("请输入选项:");
    
    string choice = Console.ReadLine();
    
    switch (choice)
    {
        case "1":
            Console.WriteLine($"当前余额:{balance:C}");  // :C 货币格式
            break;
            
        case "2":
            Console.Write("请输入存款金额:");
            if (double.TryParse(Console.ReadLine(), out double depositAmount) && depositAmount > 0)
            {
                balance += depositAmount;
                Console.WriteLine($"存款成功!当前余额:{balance:C}");
            }
            else
            {
                Console.WriteLine("无效金额!");
            }
            break;
            
        case "3":
            Console.Write("请输入取款金额:");
            if (double.TryParse(Console.ReadLine(), out double withdrawAmount) && withdrawAmount > 0)
            {
                if (withdrawAmount <= balance)
                {
                    balance -= withdrawAmount;
                    Console.WriteLine($"取款成功!当前余额:{balance:C}");
                }
                else
                {
                    Console.WriteLine("余额不足!");
                }
            }
            else
            {
                Console.WriteLine("无效金额!");
            }
            break;
            
        case "4":
            isRunning = false;
            Console.WriteLine("感谢使用,再见!");
            break;
            
        default:
            Console.WriteLine("无效选项,请重新选择!");
            break;
    }
}

4.8 常见错误与陷阱

错误1:死循环

csharp

// ❌ 错误:条件永远为 true
int i = 0;
while (i < 10)
{
    Console.WriteLine(i);
    // 忘记写 i++
}

// ❌ 错误:分号位置错误
for (int i = 0; i < 10; i++);  // 多了一个分号,循环体为空
{
    Console.WriteLine(i);  // 只执行一次,且 i 不可见
}

错误2:if 后面多分号

csharp

int x = 10;
if (x > 5);  // 多了一个分号,if 语句结束了
{
    Console.WriteLine("x大于5");  // 这个代码块总是执行
}

错误3:switch 忘记 break

csharp

int day = 2;
switch (day)
{
    case 1:
        Console.WriteLine("周一");
        // 忘记 break,会"穿透"到下一个 case
    case 2:
        Console.WriteLine("周二");
        break;
}
// 输出:周一 周二(不是预期的结果)

错误4:浮点数作为循环条件

csharp

// ❌ 浮点数不精确,可能导致多一次或少一次循环
for (double d = 0.0; d != 1.0; d += 0.1)
{
    Console.WriteLine(d);  // 永远不会到 1.0,死循环
}

// ✅ 使用整数控制循环
for (int i = 0; i <= 10; i++)
{
    double d = i * 0.1;
}

错误5:赋值 vs 比较

csharp

int x = 5;
if (x = 10)  // 赋值,不是比较,编译错误
{
    // ...
}

if (x == 10)  // ✅ 正确

4.9 本章总结

核心知识点导图

text

流程控制
├── 选择结构
│   ├── if
│   ├── if-else
│   ├── if-else if-else
│   ├── 嵌套if
│   └── switch-case
│
├── 循环结构
│   ├── for(确定次数)
│   ├── while(不确定次数,先判断)
│   ├── do-while(至少执行一次)
│   └── foreach(遍历集合)
│
└── 跳转语句
    ├── break(跳出循环)
    ├── continue(跳过本次)
    └── goto(不推荐)

选择哪种循环?

场景 推荐循环
知道确切循环次数 for
不知道次数,知道继续条件 while
至少需要执行一次 do-while
遍历数组/集合 foreach

4.10 练习题

基础题

  1. 用 if-else 写一个程序,判断用户输入的数字是正数、负数还是零。

  2. 用 switch 写一个程序,输入数字1-7,输出对应的星期几(1=周一...7=周日)。

  3. 用 for 循环输出 1 到 100 中所有 3 的倍数。

  4. 用 while 循环计算 1 到 100 的累加和。

应用题

  1. 打印如下图案(输入行数 n):

    text

    *
    **
    ***
    ****
    *****
  2. 打印如下图案(输入行数 n):

    text

        *
       ***
      *****
     *******
  3. 写一个程序,判断输入的数字是否是质数。

  4. 写一个程序,输出斐波那契数列的前 n 项(0, 1, 1, 2, 3, 5, 8, 13...)。

挑战题

  1. 写一个"数字反转"程序:

    • 输入:12345

    • 输出:54321

    • 提示:用 % 10 获取最后一位,用 / 10 去掉最后一位

  2. 写一个"水仙花数"查找程序:

    • 水仙花数:各位数字的立方和等于本身

    • 例如:153 = 1³ + 5³ + 3³

    • 找出所有的三位数水仙花数

  3. 用循环嵌套实现一个"万年历日历"输出(输入年份和月份,输出该月的日历表)。

  4. 写一个"简易银行系统":

    • 支持开户(存钱)

    • 支持存款、取款

    • 支持转账

    • 支持查询余额

    • 使用循环菜单 + switch

更多推荐