为什么需要循环?
循环是编程中的基本控制结构,它允许我们根据指定的条件,重复执行一段代码。这极大地提高了编程效率,避免了大量重复的代码。
比如在游戏脚本中,我们需要按下“W”键使人物向前移动,但是我们的人物必须是活动的,而不是平移,我们就要用到循环。在满足按下“W”键这个条件时,进行循环播放动画,达到人物看起来真的在运动一样。
for 循环:计数型循环
for 循环通常用于已知循环次数或需要按照特定步长迭代的情况。它的结构比较紧凑,包含了循环控制的三个关键部分。
基本语法:
for (initialization; condition; iterator)
{
// 循环体:需要重复执行的代码
}
- initialization (初始化): 在循环开始前只执行一次。通常在这里声明并初始化一个循环控制变量(计数器),比如 int i = 0;。
- condition (条件): 每次循环迭代开始前都会检查这个条件。如果条件为 true,则执行循环体;如果为 false,则循环结束,跳出 for 循环。
- iterator (迭代器/更新): 每次循环体执行完毕后执行。通常在这里更新循环控制变量,比如 i++(增加 1)或 i--(减少 1)或 i += 2(增加 2)。
执行流程:
- 执行 initialization。
- 检查 condition。
- 如果 condition 为 true:
a. 执行循环体内的代码。
b. 执行 iterator。
c. 回到第 2 步,继续检查 condition。 - 如果 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 循环。
执行流程:
- 检查 condition。
- 如果 condition 为 true:
a. 执行循环体内的代码。
b. 回到第 1 步,继续检查 condition。 - 如果 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 后面有分号 ;
执行流程:
- 执行循环体内的代码。
- 检查 condition。
- 如果 condition 为 true:
a. 回到第 1 步,继续执行循环体。 - 如果 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 <= 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 <= 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 <= 9; i++) // 外层循环控制行 (被乘数)
{
for (int j = 1; j <= i; j++) // 内层循环控制列 (乘数,j <= i 保证只打印下三角)
{
// 使用占位符和格式化字符串对齐输出
// {j}* {i} = {i * j, -2} 其中 ,-2 表示左对齐,宽度至少为 2
Console.Write($"{j}*{i}={i * j,-2} "); // 使用 Write 而不是 WriteLine,让结果在同一行
}
Console.WriteLine(); // 每行结束后换行
}
外层循环每执行一次,内层循环会完整地执行一遍。
学了那么久了,一定要自己动手实践一下!
可以创建新项目,也可以注释之前的练习,在原本的项目下练习:
- 求和: 使用 for 循环计算 1 到 100 之间所有偶数的和。
- 猜数字游戏 (简化版):
- 程序先设定一个固定的数字(比如 int secretNumber = 7;)。
- 使用 while 或 do-while 循环,不断提示用户输入猜测的数字。
- 每次用户输入后,告诉用户是猜大了、猜小了还是猜对了。
- 如果猜对了,使用 break 退出循环,并显示祝贺信息。
- 打印图形: 使用嵌套 for 循环打印一个 5x5 的星号 * 方阵。
*****
*****
*****
*****
*****
- (挑战) 打印一个直角三角形:
*
**
***
****
*****
(提示:内层循环的结束条件与外层循环的变量有关)
Comments NOTHING