第五阶段:流程控制.循环语句

QcrTiMo 发布于 9 天前 5 次阅读


 为什么需要循环?

循环是编程中的基本控制结构,它允许我们根据指定的条件,重复执行一段代码。这极大地提高了编程效率,避免了大量重复的代码。

比如在游戏脚本中,我们需要按下“W”键使人物向前移动,但是我们的人物必须是活动的,而不是平移,我们就要用到循环。在满足按下“W”键这个条件时,进行循环播放动画,达到人物看起来真的在运动一样。

for 循环:计数型循环

for 循环通常用于已知循环次数需要按照特定步长迭代的情况。它的结构比较紧凑,包含了循环控制的三个关键部分。

基本语法:

for (initialization; condition; iterator)
{
    // 循环体:需要重复执行的代码
}
  • initialization (初始化): 在循环开始前只执行一次。通常在这里声明并初始化一个循环控制变量(计数器),比如 int i = 0;。
  • condition (条件): 每次循环迭代开始前都会检查这个条件。如果条件为 true,则执行循环体;如果为 false,则循环结束,跳出 for 循环。
  • iterator (迭代器/更新): 每次循环体执行完毕后执行。通常在这里更新循环控制变量,比如 i++(增加 1)或 i--(减少 1)或 i += 2(增加 2)。

执行流程:

  1. 执行 initialization。
  2. 检查 condition。
  3. 如果 condition 为 true:
    a. 执行循环体内的代码。
    b. 执行 iterator。
    c. 回到第 2 步,继续检查 condition。
  4. 如果 condition 为 false:
    a. 跳出 for 循环,执行循环后面的代码。

代码示例 1:打印数字 1 到 5

Console.WriteLine("使用 for 循环打印 1 到 5:");
for (int i = 1; i <= 5; i++) // 1. 初始化 i=1; 2. 检查 i<=5; 3. 执行循环体; 4. 执行 i++; 5. 回到步骤 2
{
    Console.WriteLine(i);
}
Console.WriteLine("循环结束!");

代码输出:

使用 for 循环打印 1 到 5:
1
2
3
4
5
循环结束!

代码示例 2:计算 1 到 10 的整数和

int sum = 0; // 用于累加和
for (int i = 1; i <= 10; i++)
{
    sum += i; // 相当于 sum = sum + i;
    Console.WriteLine($"当前 i={i}, 当前 sum={sum}"); // 可以看到累加过程
}
Console.WriteLine($"1 到 10 的和是:{sum}"); // 输出 55

代码示例 3:反向打印 5 到 1

Console.WriteLine("使用 for 循环反向打印 5 到 1:");
for (int i = 5; i >= 1; i--)
{
    Console.WriteLine(i);
}

while 循环:条件型循环

while 循环用于当某个条件持续满足时,就一直重复执行代码块。你可能不知道具体要循环多少次,只知道当条件不再满足时就停止。

基本语法:

while (condition)
{
    // 循环体:条件为 true 时重复执行的代码
    // **重要:** 循环体内通常需要包含能改变 condition 结果的语句,否则可能导致死循环!
}
  • condition (条件): 每次循环迭代开始前检查。如果为 true,执行循环体;如果为 false,跳出 while 循环。

执行流程:

  1. 检查 condition。
  2. 如果 condition 为 true:
    a. 执行循环体内的代码。
    b. 回到第 1 步,继续检查 condition。
  3. 如果 condition 为 false:
    a. 跳出 while 循环,执行循环后面的代码。

代码示例 1:使用 while 打印 1 到 5

Console.WriteLine("使用 while 循环打印 1 到 5:");
int counter = 1; // 1. 初始化计数器 (在循环外)
while (counter <= 5) // 2. 检查条件
{
    Console.WriteLine(counter);
    counter++; // 3. 更新计数器 (在循环体内,非常重要!)
}
Console.WriteLine("循环结束!");

这个例子实现了和 for 循环相同的功能,但你需要手动在循环体内部更新计数器 counter。

代码示例 2:模拟用户输入,直到输入 "exit"

string userInput = ""; // 初始化为空字符串
Console.WriteLine("请输入命令,输入 'exit' 退出:");
while (userInput != "exit") // 只要用户输入的不是 "exit" 就一直循环
{
    Console.Write("> "); // 提示符
    userInput = Console.ReadLine(); // 读取用户输入
    // 可以根据输入做些事情 (这里只是简单打印)
    if (userInput != "exit")
    {
        Console.WriteLine($"你输入了:{userInput}");
    }
}
Console.WriteLine("程序退出。");

避免死循环!

如果 while 的条件永远为 true,那么循环将永远不会结束,程序会卡住,这就是死循环 (Infinite Loop)。在使用 while 时,必须确保循环体内的代码最终能让条件变为 false。

// 这是一个死循环的例子!不要轻易运行!
int i = 0;
while (i < 10) // 条件永远为 true
{
    Console.WriteLine("死循环中...");
    // 错误:忘记更新 i 的值了! i++ 应该在这里
}

do-while 循环:至少执行一次

do-while 循环与 while 非常相似,唯一的关键区别在于:do-while 先执行一次循环体,然后再检查条件。这意味着,无论条件是否满足,do-while 循环体至少会执行一次

基本语法:

do
{
    // 循环体:至少执行一次的代码
    // 同样需要注意更新条件相关的变量
} while (condition); // 注意:while 后面有分号 ;

执行流程:

  1. 执行循环体内的代码。
  2. 检查 condition。
  3. 如果 condition 为 true:
    a. 回到第 1 步,继续执行循环体。
  4. 如果 condition 为 false:
    a. 跳出 do-while 循环,执行循环后面的代码。

代码示例:请求用户输入,直到输入有效数字

int number;
bool isValidInput;
do
{
    Console.Write("请输入一个 1 到 10 之间的整数:");
    string input = Console.ReadLine();
    // 尝试将输入转换为整数,TryParse 会返回 true 或 false 表示是否成功
    isValidInput = int.TryParse(input, out number);
    if (isValidInput)
    {
        // 检查数字范围
        if (number < 1 || number > 10)
        {
            Console.WriteLine("输入无效,数字必须在 1 到 10 之间。");
            isValidInput = false; // 标记为无效,继续循环
        }
    }
    else
    {
        Console.WriteLine("输入无效,请输入一个整数。");
    }
} while (!isValidInput); // 只要输入无效 (!isValidInput == true),就继续循环
Console.WriteLine($"你输入的有效数字是:{number}");

在这个例子中,我们至少需要向用户请求一次输入,所以 do-while 很合适。int.TryParse() 是一个很有用的方法,它尝试将字符串转换为整数,如果成功返回 true 并将结果存入 out 参数(这里是 number),失败则返回 false。

switch 语句 - 多路选择

为什么需要 switch 语句?

想象一下,你有一个变量,需要根据它的具体值来执行不同的操作。比如,根据用户输入的数字(1代表查询,2代表添加,3代表删除)来调用不同的功能。你可以用一长串的 if-else if-else 来实现:

int choice = GetUserChoice(); // 假设这个方法返回用户输入的数字
if (choice == 1)
{
    QueryData();
}
else if (choice == 2)
{
    AddData();
}
else if (choice == 3)
{
    DeleteData();
}
else if (choice == 4)
{
    UpdateData();
}
else
{
    Console.WriteLine("无效的选择!");
}

当选项很多,并且都是基于同一个变量的值进行判断时,if-else if 结构会显得有些冗长和重复。这时,switch 语句提供了一种更清晰、更结构化的替代方案。

switch 语句专门用于: 检查一个表达式的值,并将该值与一系列 case 标签中指定的常量值进行匹配。当找到匹配项时,执行该 case 标签下的代码块。

switch 语句的基本语法

switch (expression) // expression 是要评估的表达式,通常是一个变量
{
    case constantValue1: // constantValue1 是一个常量值 (如 1, 'A', "hello")
        // 当 expression 的值等于 constantValue1 时执行这里的代码
        // ...
        break; // 非常重要!执行完当前 case 后跳出 switch 语句
    case constantValue2:
        // 当 expression 的值等于 constantValue2 时执行这里的代码
        // ...
        break;
    case constantValue3:
    case constantValue4: // 可以将多个 case 标签叠在一起,表示共享同一段代码
        // 当 expression 的值等于 constantValue3 或 constantValue4 时执行这里的代码
        // ...
        break;
    // ... 可以有更多的 case 标签 ...
    default: // 可选的,当 expression 的值与上面所有 case 都不匹配时执行
        // ...
        break; // default 里的 break 也是必需的 (除非它是 switch 的最后一块)
}
// break 会使程序跳转到这里继续执行

关键部分解释:

  • switch (expression):
    • expression 是需要被评估的表达式,它的结果类型通常是整数 (int, byte, char 等)、枚举 (enum,我们稍后会学) 或字符串 (string)。从 C# 7 开始,也可以用于更复杂的模式匹配,但我们先关注基础用法。
  • case constantValue::
    • case 是关键字。
    • constantValue 必须是一个常量表达式。这意味着它必须是编译器在编译时就能确定其值的东西,例如:
      • 字面量 (Literals): 10, 'A', "Option1"
      • 用 const 定义的常量: const int MaxValue = 100; case MaxValue:
      • 枚举成员 (Enum members): case DayOfWeek.Monday:
    • case 标签后面的冒号 : 是必需的。
    • 每个 case 标签的值在同一个 switch 语句中必须是唯一的。
  • 代码块: 跟在 case 标签(或多个叠在一起的标签)后面的代码。
  • break;:
    • 至关重要! break 语句用于立即跳出整个 switch 结构。
    • 在 C# 中,不允许从一个 case 块隐式地“贯穿 (fall through)” 到下一个 case 块(这与 C/C++ 不同,可以防止意外错误)。
    • 因此,每个非空的 case 代码块的末尾(以及 default 块的末尾)通常都必须以 break; 结束。(其他合法的结束方式包括 return;, throw; 或 goto case; / goto default; / goto label;,但 break 是最常见的)。
    • 如果多个 case 标签共享同一段代码(像上面的 case constantValue3: 和 case constantValue4:),只需要在共享代码块的末尾写一个 break;。
  • default::
    • default 标签是可选的
    • 它的代码块会在 expression 的值与所有 case 标签的值都不匹配时执行。
    • 类似于 if-else if-else 结构中的最后一个 else。
    • default 标签可以放在 switch 语句中的任何位置(不一定非要在最后),但习惯上放在最后。无论放在哪里,它只有在所有 case 都不匹配时才会被评估。
    • default 块后面通常也需要 break;。

代码示例1:根据数字选择操作

Console.WriteLine("请输入操作选项 (1: 查询, 2: 添加, 3: 删除):");
string input = Console.ReadLine();
int choice;
// 尝试将输入转换为整数
if (int.TryParse(input, out choice))
{
    switch (choice)
    {
        case 1:
            Console.WriteLine("正在执行查询操作...");
            // QueryData(); // 假设调用查询方法
            break; // 执行完 case 1 后跳出 switch
        case 2:
            Console.WriteLine("正在执行添加操作...");
            // AddData(); // 假设调用添加方法
            break; // 执行完 case 2 后跳出 switch
        case 3:
            Console.WriteLine("正在执行删除操作...");
            // DeleteData(); // 假设调用删除方法
            break; // 执行完 case 3 后跳出 switch
        default: // 如果输入的数字不是 1, 2 或 3
            Console.WriteLine("无效的操作选项!请输入 1, 2 或 3。");
            break;
    }
}
else // 如果输入无法转换为整数
{
    Console.WriteLine("输入无效,请输入一个数字。");
}
Console.WriteLine("Switch 语句执行完毕。");

代码示例2:根据星期几输出信息(使用DayOfWeek 枚举)

DayOfWeek 是 .NET 内置的一个枚举类型,代表一周中的某一天。

DayOfWeek today = DateTime.Today.DayOfWeek; // 获取今天是星期几
Console.WriteLine($"今天是:{today}");
switch (today)
{
    case DayOfWeek.Saturday: // 星期六
    case DayOfWeek.Sunday:   // 星期日
        Console.WriteLine("今天是周末,好好休息!");
        break; // 周六和周日共享这个代码块
    case DayOfWeek.Monday:
        Console.WriteLine("周一,开始新的一周工作/学习!");
        break;
    case DayOfWeek.Friday:
        Console.WriteLine("周五啦,马上就周末了!");
        break;
    default: // 其他工作日 (周二, 周三, 周四)
        Console.WriteLine("工作日,继续努力!");
        break;
}

代码示例 3:根据字符判断元音/辅音

Console.Write("请输入一个英文字母:");
char letter = Console.ReadKey().KeyChar; // 读取一个字符输入
Console.WriteLine(); // 换行
// 将字符转换为小写,方便比较
letter = char.ToLower(letter);
switch (letter)
{
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        Console.WriteLine($"{letter} 是元音字母。");
        break;
    default:
        // 这里可以再加一个判断,确保输入的是字母
        if (letter >= 'a' && letter <= 'z')
        {
            Console.WriteLine($"{letter} 是辅音字母。");
        }
        else
        {
            Console.WriteLine("输入不是一个有效的英文字母。");
        }
        break;
}

何时使用 switch vs. if-else if?

  • 使用 switch 的场景:
    • 当你需要根据一个单一表达式的值,从多个固定(常量)选项中选择一个执行路径时。
    • 当判断条件是等于 (==) 某个常量值时。
    • 当选项数量较多时,switch 通常比冗长的 if-else if 结构更清晰、更易读
  • 使用 if-else if 的场景:
    • 当判断条件不仅仅是等于,还包括范围判断 (>, <, >=, <=) 或更复杂的逻辑组合 (&&, ||, !) 时。
    • 当每次判断的变量或表达式不同时。
    • 当只有两三个选项时,if-else 可能更简洁。

break 和 continue:控制循环流程

有时我们需要在循环过程中提前退出循环,或者跳过当前这次循环迭代,进入下一次。

  • break: 立即终止包含它的最内层循环(for, while, do-while 或 switch 语句,我们后面会学 switch),程序将跳转到循环体之后的第一条语句继续执行。
  • continue: 立即结束当前这次循环迭代,跳过循环体中 continue 之后的剩余语句,然后直接进入下一次循环迭代的判断(对于 for 循环,会先执行迭代器部分 i++ 再判断条件)。

代码示例 (break):找到第一个能被 7 整除的数就停止

Console.WriteLine("寻找 1 到 20 中第一个能被 7 整除的数:");
for (int i = 1; i &lt;= 20; i++)
{
    Console.WriteLine($"正在检查: {i}");
    if (i % 7 == 0)
    {
        Console.WriteLine($"找到了!{i} 能被 7 整除。");
        break; // 找到后立即跳出 for 循环
    }
}
Console.WriteLine("搜索结束。");

代码示例 (continue):只打印 1 到 10 中的奇数

Console.WriteLine("打印 1 到 10 中的奇数:");
for (int i = 1; i &lt;= 10; i++)
{
    if (i % 2 == 0) // 如果 i 是偶数
    {
        continue; // 跳过本次循环剩下的代码 (Console.WriteLine),直接进入下一次迭代 (i++)
    }
    Console.WriteLine(i); // 这行代码只有在 i 不是偶数 (即 i 是奇数) 时才会被执行
}

嵌套循环

你可以在一个循环的内部再包含另一个循环,形成嵌套循环。这常用于处理二维结构(如表格、矩阵)或者需要多层迭代的情况。

代码示例:打印 9x9 乘法表

Console.WriteLine("打印 9x9 乘法表:");
for (int i = 1; i &lt;= 9; i++) // 外层循环控制行 (被乘数)
{
    for (int j = 1; j &lt;= i; j++) // 内层循环控制列 (乘数,j &lt;= i 保证只打印下三角)
    {
        // 使用占位符和格式化字符串对齐输出
        // {j}* {i} = {i * j, -2}  其中 ,-2 表示左对齐,宽度至少为 2
        Console.Write($"{j}*{i}={i * j,-2}  "); // 使用 Write 而不是 WriteLine,让结果在同一行
    }
    Console.WriteLine(); // 每行结束后换行
}

外层循环每执行一次,内层循环会完整地执行一遍。

学了那么久了,一定要自己动手实践一下!

可以创建新项目,也可以注释之前的练习,在原本的项目下练习:

  1. 求和: 使用 for 循环计算 1 到 100 之间所有偶数的和。
  2. 猜数字游戏 (简化版):
    • 程序先设定一个固定的数字(比如 int secretNumber = 7;)。
    • 使用 while 或 do-while 循环,不断提示用户输入猜测的数字。
    • 每次用户输入后,告诉用户是猜大了、猜小了还是猜对了。
    • 如果猜对了,使用 break 退出循环,并显示祝贺信息。
  3. 打印图形: 使用嵌套 for 循环打印一个 5x5 的星号 * 方阵。
*****
*****
*****
*****
*****
  1. (挑战) 打印一个直角三角形:
*
**
***
****
*****

(提示:内层循环的结束条件与外层循环的变量有关)

斯哈斯哈斯哈,佳代子啊啊啊啊啊啊ᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗ
最后更新于 2025-05-19