C# 入门课提醒

1、查看作业代码请访问:观看作业示例
作者不建议你直接照抄代码,请仔细阅读文章,做作业,并思考代码背后的逻辑。

2、本文章不能完全覆盖考点和课本。只能做一个参考,作快速入门用。

📚 C# 新手学习大纲 (第 1 课 - 第 92 课)

回到顶部

🚀 第一阶段:基础语法与流程控制 (L1 - L19)

  1. 第 1 课:编程环境与 C# 简介
  2. 第 2 课:Visual Studio 基础使用
  3. 第 3 课:C# 世界的“你好”
  4. 第 4 课:注释与代码规范-的重要性)
  5. 第 5 课:基本数据类型 (int, string)
  6. 第 6 课:变量 (Variables) 简介
  7. 第 7 课:显示变量里的数据 与 更多的数字类型
  8. 第 8 课:让数字动起来——算术运算符
  9. 第 9 课:变量的快速修改——复合赋值运算符
  10. 第 10 课:最简洁的修改——递增与递减运算符
  11. 第 11 课:布尔值和关系运算符
  12. 第 12 课:组合条件——逻辑运算符
  13. 第 13 课:程序的岔路口——条件语句 if-else
  14. 第 14 课:简洁的多值判断——switch 语句
  15. 第 15 课:重复执行任务——while 循环
  16. 第 16 课:专业的计数循环——for 循环
  17. 第 17 课:至少执行一次的循环——do-while 循环
  18. 第 18 课:控制循环流:breakcontinue
  19. 第 19 课:嵌套循环

💻 第二阶段:Console 进阶与桌面应用基础 (L20 - L47)

  1. 第 20 课:数组 (Arrays) 基础 I
  2. 第 21 课:附加课 I:美化你的 Console,如何设计 Tui 界面?

以下内容暂未完成

  1. 第 22 课:程序启动的秘密——如何给 Main 函数传递参数?
  2. 第 23 课:桌面应用简介:WinForms 是什么?

💡 第 1 课:C# 是什么?.NET 是什么?

📚 核心知识点

  • C# (C-Sharp) 是一种 面向对象 的高级编程语言。
  • C# 由微软开发,它很像 Java 和 C++,主要用来开发 Windows 应用、Web 服务、手机 App 和游戏 (Unity)。
  • .NET 是 C# 程序的 运行环境和框架
  • CLR (Common Language Runtime) 是 .NET 的核心组件,负责把 C# 代码翻译给电脑执行 (就像一个翻译官)。

🛠️ 第 2 课:写 C# 代码需要啥工具?

📚 核心知识点

要写 C# 代码,你需要两个核心工具:

  1. 代码编辑器 / 集成开发环境 (IDE)
    • 推荐Visual Studio (VS)。它是微软的旗舰工具,集成了写代码、编译、调试等所有功能。
  2. .NET SDK (软件开发工具包)
    • 这是 C# 程序的 发动机 和工具箱,包含了编译器和运行环境。
    • 你需要安装它,才能编译和运行 C# 代码。

✍️ 第 3 课:C# 世界的“你好”

📚 核心知识点

  • 程序从 static void Main(string[] args) 这个方法开始执行,这是程序的 入口
  • 我们用 Console.WriteLine("..."); 语句来向屏幕 (控制台) 输出文本。
  • 我们用 Console.ReadKey(); 语句来暂停程序,等待用户按键,这样才能看到输出结果。
  • 控制台应用 (Console App) 是学习编程基础的最佳起点。

🔧 准备工作:创建你的第一个项目

在开始写代码之前,你需要先创建一个C#控制台应用项目:

  1. 打开Visual Studio
  2. 创建新项目 → 选择"控制台应用"(Console App)
  3. 给项目起名 → 比如叫"HelloWorld"
  4. 选择位置 → 选择一个你容易找到的文件夹

创建完成后,Visual Studio会自动生成一个基础的Program.cs文件,里面已经有一些模板代码。


1、认识程序的基本结构

让我们先看看一个最简单的C#程序长什么样:

1
2
3
4
5
6
7
8
9
10
11
12
using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
// 这里是我们要写代码的地方
}
}
}

代码解释:

  • using System; - 引入系统命名空间,这样我们才能使用Console类
  • namespace HelloWorld - 命名空间,就像给代码起个"姓"
  • class Program - 类,C#程序的基本组织单位
  • static void Main(string[] args) - 程序的入口点,所有C#程序都从这里开始执行!

2、写出你的第一行代码

现在,让我们在Main方法里写下第一行代码:

1
2
3
4
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}

这行代码做什么?

  • Console - 控制台类,代表我们的命令行窗口
  • WriteLine - 写一行文字的方法
  • "Hello World!" - 我们要显示的文字内容
  • () - 方括号,用来传递参数(这里传递的是要显示的文字)
  • ; - 分号,表示这行代码结束了

运行结果:
当你按下F5(或点击"启动"按钮)运行程序时,你会看到一个黑色的窗口闪一下就消失了,上面显示着"Hello World!"。


3、让程序停下来

刚才的程序为什么一闪而过?因为程序执行完就自动关闭了。让我们加上一行代码,让程序等待我们按任意键再退出:

1
2
3
4
5
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}

新增代码解释:

  • Console.ReadKey() - 等待用户按任意键,然后继续执行
  • 这行代码会让程序暂停,直到你按下一个键

现在再运行程序:

  1. 按F5运行
  2. 你会看到黑色的控制台窗口显示"Hello World!"
  3. 窗口会一直显示,不会自动关闭
  4. 按任意键,程序才会结束

4、让输出更丰富

让我们来输出更多内容,让程序更有趣:

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
Console.WriteLine("=================================");
Console.WriteLine("欢迎来到 C# 编程世界!");
Console.WriteLine("这是我的第一个程序");
Console.WriteLine("Hello World!");
Console.WriteLine("=================================");

Console.WriteLine("\\n按任意键退出程序...");
Console.ReadKey();
}

新增技巧:

  • \\n - 换行符,会在输出时产生一个空行
  • 使用等号 = 可以创建漂亮的分隔线
  • 多行输出可以创建更丰富的界面效果

5、与程序互动

让我们让程序变得更智能,可以和用户进行简单的互动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
Console.WriteLine("=================================");
Console.WriteLine("欢迎来到 C# 编程世界!");
Console.WriteLine("Hello World!");
Console.WriteLine("=================================");

// 询问用户的名字
Console.Write("请告诉我你的名字:");
string userName = Console.ReadLine();

// 个性化问候
Console.WriteLine($"你好,{userName}!欢迎学习C#编程!");
Console.WriteLine("让我们一起开始这段精彩的编程之旅吧!");

Console.WriteLine("\\n按任意键退出程序...");
Console.ReadKey();
}

新知识点:

  • Console.Write() - 输出文字但不换行
  • Console.ReadLine() - 读取用户输入的一行文字
  • string userName - 声明一个字符串变量来存储用户的名字
  • $"{userName}" - 字符串插值,把变量的值嵌入到字符串中

6、理解代码的执行流程

让我们用注释来详细解释每一行代码的作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System; // 引入系统命名空间,这样我们才能使用Console类

namespace HelloWorld // 定义命名空间,避免命名冲突
{
class Program // 定义一个类,所有代码都要写在类里面
{
static void Main(string[] args) // 程序的入口点,从这里开始执行
{
// 1. 显示欢迎界面
Console.WriteLine("=================================");
Console.WriteLine("欢迎来到 C# 编程世界!");
Console.WriteLine("Hello World!");
Console.WriteLine("=================================");

// 2. 与用户互动
Console.Write("请告诉我你的名字:"); // 提示用户输入
string userName = Console.ReadLine(); // 读取用户输入并存储

// 3. 个性化问候
Console.WriteLine($"你好,{userName}!欢迎学习C#编程!");
Console.WriteLine("让我们一起开始这段精彩的编程之旅吧!");

// 4. 等待用户按键退出
Console.WriteLine("\\n按任意键退出程序...");
Console.ReadKey(); // 暂停程序,等待用户按键
}
}
}

🎪 完整的最终代码

这是你这节课的最终成果,一个功能完整的Hello World程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("╔══════════════════════════════════════╗");
Console.WriteLine("║ 欢迎来到 C# 编程世界! ║");
Console.WriteLine("║ Hello World! ║");
Console.WriteLine("╚══════════════════════════════════════╝");

Console.Write("\\n请告诉我你的名字:");
string userName = Console.ReadLine();

Console.WriteLine($"\\n🎉 你好,{userName}!");
Console.WriteLine("🚀 恭喜你成功运行了第一个C#程序!");
Console.WriteLine("💻 你已经迈出了成为程序员的第一步!");
Console.WriteLine("📚 接下来让我们一起探索更多编程的奥秘吧!");

Console.WriteLine("\\n按任意键退出程序...");
Console.ReadKey();
}
}
}

🎓 本课总结

通过这节课的学习,你已经掌握了:

  1. 程序结构 - 理解了C#程序的基本组成部分
  2. 输出文字 - 学会了使用Console.WriteLine()显示内容
  3. 程序暂停 - 掌握了Console.ReadKey()控制程序流程
  4. 用户输入 - 体验了Console.ReadLine()与用户互动
  5. 变量使用 - 初步接触了字符串变量的声明和使用
  6. 字符串插值 - 学会了用$"{变量}"的方式格式化输出

恭喜你! 你已经成功写出了第一个C#程序,这是你编程之路的重要里程碑!继续保持这份热情,下一节课我们将学习更多有趣的编程知识。


第三课课堂作业

  1. 基础练习:修改程序,让它显示你的个人信息(姓名、年龄、爱好等)
  2. 互动增强:让程序询问用户的多个信息(如年龄、城市),然后给出个性化的回应(ReadLine())

记住,编程最重要的是多练习!不要怕犯错,每个错误都是学习的机会。加油!💪


🛑 第 4 课:语句与分号 😉 的重要性

📚 核心知识点

  1. 语句 (Statement):程序中一个完整的动作或命令。
  2. 分号 (😉每个完整的语句都必须以分号结束。它就像 C# 语句的“句号”。忘记分号会导致语法错误。
  3. 区分大小写 (Case-Sensitive):C# 严格区分大小写。 Consoleconsole 是两个不同的东西。

💬 第 5 课:代码的“旁白”——注释

📚 核心知识点

  • 注释 是写给程序员看的说明文字,编译器会忽略它,不影响程序运行。
  • 单行注释:使用 //。它只注释掉当前行的剩余部分。
  • 多行注释:使用 /* ... */。它注释掉 /* */ 之间的所有内容。
  • 作用:提高代码可读性,解释复杂逻辑,或临时禁用代码。

📦 第 6 课:数据存哪里?——变量 (Variables) 简介

📚 核心知识点

  1. 变量:存储数据的“容器”或“小盒子”。
  2. 强类型:C# 要求在声明变量时 必须 指定数据类型。
  3. 声明与赋值
    • 声明数据类型 变量名; (如 int age;)
    • 赋值变量名 = 值; (如 age = 25;)
  4. 常用基本类型
    • int:用于存储 整数 (如 100, -5)。
    • string:用于存储 文本/字符串 (如 “锁那”),文本必须用 双引号 "" 括起来。
  5. 命名规范:变量推荐使用 小驼峰命名法 (camelCase),如 loveSinger

第六课课堂作业

🎯 学习目标

掌握 C# 中变量的声明初始化过程,并正确使用 intstring 这两种基本数据类型。

📄 题目要求

  1. 声明一个 int 变量
    • 变量名:sanaYears
    • 作用:用于存储歌手出道的年份。
    • 初始值:2015
  2. 声明一个 string 变量
    • 变量名:sanaName
    • 作用:用于存储歌手的姓名。
    • 初始值:"锁那"
  3. 最终要求:提交包含以上两个变量的声明和初始化语句的 C# 代码片段,并在末尾加上 Console.ReadKey(); 以便在控制台查看。

🖥️ 第 7 课:显示变量里的数据 与 更多的数字类型

📚 核心知识点 (两部分内容)

(A) 输出变量

  • 直接输出:将变量名放在 Console.WriteLine() 括号内即可。
  • 字符串拼接:使用 加号 + 将文本和变量的值连接起来,形成一个完整的句子。
    • Console.WriteLine("我的年龄是:" + age);

(B) 更多的数字类型

  • double:标准浮点数 (带小数点的数字),如 98.5。这是 C# 默认的小数类型。
  • decimal:高精度十进制数,用于货币和金融计算。赋值时必须加 m 后缀,如 19.99m

第七课课堂作业

🎯 学习目标

  1. 掌握使用加号 (+) 将字符串和变量值拼接输出。
  2. 理解并正确使用非整数的数字类型:doubledecimal

📄 题目要求

  1. 初始化变量
    • 声明一个 int 变量 sanaYears,赋值为 2015
    • 声明一个 double 变量 sanaRating,赋值为 9.9
    • 声明一个 decimal 变量 sanaPrice,赋值为 350.5m。(注意 m 后缀
  2. 输出结果
    • 使用 Console.WriteLine() 语句,将 sanaYears 的值和一段说明文字拼接后输出。
    • 再次使用 Console.WriteLine(),将 sanaRatingsanaPrice 的值以及各自的说明文字拼接后输出。
  3. 暂停:在程序末尾添加 Console.ReadKey();

➕ 第 8 课:让数字动起来——算术运算符

📚 核心知识点

  1. 基本算术符+, -, *, /, %
  2. 取模运算 (%):求除法运算后的余数。例如:10 % 3 结果是 1。
  3. 整数除法陷阱
    • 当两个 int (整数) 相除时,结果也是 int,小数部分会被丢弃 (例如 10 / 4 结果是 2)。
    • 强制精确除法:需要使用强制类型转换 (Casting)
      1
      2
      3
      int a = 17;
      int b = 5;
      double result = (double)a / b; // 结果为 3.4

第八课课堂作业

目标

演示所有算术运算,并解决整数除法陷阱。

要求

  1. 初始化变量
    • 声明两个 int 变量 num1num2,分别赋值为 175
  2. 算术运算
    • 使用 + 运算符计算并存储和 (sumResult)。
    • 使用 % 运算符计算并存储余数 (modResult)。
  3. 精确除法
    • 使用 / 运算符计算除法,但要求结果是精确的小数 (double),因此必须使用强制类型转换来解决整数除法的陷阱。
  4. 输出结果:使用 Console.WriteLine() 拼接输出所有计算结果。

🔗 第 9 课:变量的快速修改——复合赋值运算符

🎯 本课目标

通过这节课,你将学会如何创建你的第一个C#程序,向世界问好!我们会一步一步地教你写出经典的"Hello World"程序,让你真正体验到编程的乐趣。

📚 核心知识点

  • 复合赋值运算符:将算术操作符和赋值操作符 (=) 结合,用于对变量自身进行修改,使代码更简洁。
  • 常用形式
    • 加后赋值x += y 等同于 x = x + y
    • 减后赋值x -= y 等同于 x = x - y
    • 乘后赋值x *= y 等同于 x = x * y
  • 字符串拼接stringName += " World!" 可用于简洁地追加文本。

第九课课堂作业

🎯 学习目标

掌握如何使用 复合赋值运算符 (例如 +=, -=) 来简洁地对变量自身的值进行修改。

📄 题目要求

请您使用复合赋值运算符,完成以下操作:

  1. 初始化变量
    • 声明一个 int 变量 currentYear,赋值为 2015
    • 声明一个 string 变量 singerName,赋值为 "锁那"
  2. 使用 += 进行计算
    • 假设现在过去了 10 年,使用 += 运算符,让 currentYear 增加 10
  3. 使用 += 进行字符串拼接
    • 使用 += 运算符,给 singerName 变量追加文本,使它变成 "锁那 鹿乃洛天依" (假设的追加内容)。
  4. 输出结果:使用 Console.WriteLine() 拼接输出这两个变量的新值。

📈 第 10 课:最简洁的修改——递增与递减运算符

📚 核心知识点

  1. 递增 (++):将变量值加 1。
  2. 递减 (--):将变量值减 1。
  3. 前缀式 vs. 后缀式 (在表达式中使用时有区别):
    • 前缀式 (++a)先变 (先加 1),后用新值
    • 后缀式 (a++)后变 (先用旧值),后加 1
    • 记忆口诀:“前缀先变,后缀后变。”

第十课课堂作业

📚 核心知识点

本课作业旨在区分前缀式 (++a/--a) 和后缀式 (a++/a--) 在表达式中的执行顺序。

📄 题目要求

请您根据以下 C# 代码片段,推断出每一行 Console.WriteLine() 语句的输出结果,并解释原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 递增与递减 */
int a = 10, b;

// 第一步:后缀式递增
b = a++;

// 第一次输出 a, b 的值
Console.WriteLine(a);
Console.WriteLine(b);

// 第二步:前缀式递减
b = --a;

// 第二次输出 a, b 的值
Console.WriteLine(a);
Console.WriteLine(b);

// 第三步:复合赋值(额外增加)
b += 2;

// 第三次输出 a, b 的值
Console.WriteLine(a);
Console.WriteLine(b);

🧐 第 11 课:比较与判断——布尔值和关系运算符

📚 核心知识点

  1. 布尔值 (bool):只能存储两个值:true (真) 或 false (假)。这是所有程序判断的基础。
  2. 关系运算符:用于比较两个值,返回一个 bool 结果。
    • 等于== (重点:双等号!)
    • 不等于!=
    • 大于/小于>, <
    • 大于等于/小于等于>=, <=
  3. 重要区分= 是赋值== 是比较

第十一课课堂作业

🎯 目标

掌握布尔值 (bool) 的使用,以及关系运算符 (<, ==, >=) 的应用。

📄 题目要求

  1. 初始化变量
    • 声明一个 int 变量 currentYear,赋值为 2025
    • 声明一个 int 变量 targetYear,赋值为 2030
  2. 使用关系运算符
    • 声明一个 bool 变量 isFuture,用来判断 currentYear 是否小于 targetYear
    • 声明一个 bool 变量 isEqual,用来判断 currentYear 是否等于 targetYear
  3. 输出结果:使用 Console.WriteLine() 拼接输出这两个 bool 变量的结果,并加上简短的说明文字。

💡 关键提示

请务必区分赋值符号 (=) 和比较相等符号 (==)。


🚦 第 12 课:组合条件——逻辑运算符

📚 核心知识点

逻辑运算符用于组合或反转 bool 表达式:

  1. 逻辑与 (AND):&&
    • 要求:所有条件都为 true,结果才是 true
    • 口诀:【一假即假】。
  2. 逻辑或 (OR):||
    • 要求:只要有一个条件为 true,结果就是 true
    • 口诀:【一真即真】。
  3. 逻辑非 (NOT):!
    • 作用:将布尔值结果反转 (truefalsefalsetrue)。

第十二课课堂作业

🎯 学习目标

掌握如何使用逻辑运算符 (&&||!) 组合和反转布尔条件。

📄 题目要求

  1. 初始化变量
    • 声明一个 int 变量 temperature (温度),赋值为 25
    • 声明一个 bool 变量 isRaining (下雨),赋值为 true
  2. 组合条件 (AND)
    • 声明一个 bool 变量 needJacket (需要外套),判断条件是:温度小于 20 并且 下雨为真
    • 预期结果:False
  3. 组合条件 (OR 和 NOT)
    • 声明一个 bool 变量 isGoodDay (天气好),判断条件是:温度大于 30 或者 下雨为假 (即不下雨)。
    • 预期结果:True
  4. 输出结果:使用 Console.WriteLine() 拼接输出 needJacketisGoodDay 的布尔值结果,并加上说明文字。

💡 核心知识点回顾 (用于解题)

  • AND 运算符:&& (一假即假)
  • OR 运算符:|| (一真即真)
  • NOT 运算符:! (取反)

🚦 第 13 课:程序的岔路口——条件语句 if-else

📚 核心知识点

条件语句用于根据布尔结果 (truefalse) 来控制程序的执行流程。

  1. 基础 if 语句

    • 作用:如果条件满足 (true),则执行 {} 内的代码。
    • 语法
      1
      2
      3
      4
      5
      if (布尔表达式)
      {
      // 条件为 true 时执行
      }
      // 注意:if 后面不能有分号 (;)
  2. if-else 语句 (非此即彼)

    • 作用:在条件为 truefalse 时执行不同逻辑。
    • 语法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      if (条件)
      {
      // 条件为 true 时执行 (A 任务)
      }
      else
      {
      // 条件为 false 时执行 (B 任务)
      // else 是默认的、最后的选择
      }
  3. if-else if-else 语句 (多重判断)

    • 作用:依次检查多个互斥条件。
    • 执行流程:C# 从上到下依次检查,一旦遇到第一个 true 的条件,就执行对应代码块,然后跳出整个结构
    • 语法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      if (条件一)
      {
      // ...
      }
      else if (条件二)
      {
      // ...
      }
      else
      {
      // 以上所有条件都不满足时执行
      }
  4. 重要语法规则

    • if, else if, else 后面都不能直接跟分号 ;
    • if 后面的小括号 () 内必须是最终能得出 bool 结果的表达式。

第十三课课堂作业

🎯 目标

掌握使用 if-else if-else 结构,让程序根据条件进行多分支决策。

📄 题目要求

  1. 初始化变量
    • 声明一个 int 变量 currentScore (当前分数),并将其赋值为 75
  2. 创建多重判断结构
    • 编写一个完整的 if-else if-else 结构,根据 currentScore 的值进行分级判断。
  3. 判断逻辑
    • 如果分数大于等于 90,则输出 "等级:A"
    • 否则,如果分数大于等于 60,则输出 "等级:B"
    • 否则(分数小于 60),则输出 "等级:C"
  4. 最终要求:确保程序在输入 75 分时,能够正确输出 "等级:B"

🧭 第 14 课:简洁的多值判断——switch 语句

📚 核心知识点

switch 语句适用于对一个变量或表达式的精确值进行多分支匹配判断。

  1. 基本结构

    • 作用:将 switch 括号内的值与各个 case 后的值进行比较。
    • 语法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      switch (变量或表达式)
      {
      case1:
      // 如果值匹配,执行这里的代码
      break; // 必须使用 break 跳出 switch

      case2:
      // 如果值匹配,执行这里的代码
      break;

      default:
      // 如果所有 case 都不匹配,执行这里的代码
      break;
      }
  2. 类型一致性

    • 规则case 后的匹配值的数据类型,必须与 switch 括号内的变量类型保持一致
    • 示例:如果 switch (string name),则 case 必须是字符串:case "Alice":
  3. break 语句

    • 重要性:每个 case 的代码块执行完毕后,必须使用 break; 语句,否则 C# 会报错(不允许代码“穿透”到下一个 case)。
  4. default 语句

    • 作用:类似于 if-else if 结构中的最后的 else,处理所有不匹配的输入。

第十四课课堂作业

🎯 课堂作业目标

使用 switch 语句对一个字符串变量的值进行多分支匹配判断。

📄 题目要求

  1. 初始化变量
    • 声明一个 string 变量 singerName (歌手名字),赋值为 "鹿乃"
  2. 创建 switch 结构
    • 编写一个 switch 语句,以 singerName 作为判断对象。
    • Case 1:如果匹配到 "锁那",输出 "你喜欢的是锁那!"
    • Case 2:如果匹配到 "鹿乃",输出 "你喜欢的是鹿乃!"
    • Default:如果以上都不匹配,输出 "你喜欢的是其他歌手。"
  3. 关键要求
    • 确保 case 的匹配值是字符串类型 (""),与 switch 变量类型一致。
    • 确保每个 case 后面都使用了 break;

🔁 第 15 课:重复执行任务——while 循环

📚 核心知识点

while 循环是最基础的循环结构,用于让代码在满足特定条件时不断重复执行

  1. 基本结构

    • 特点“先判断,后执行”
    • 语法
      1
      2
      3
      4
      5
      6
      while (布尔表达式) // 只要条件为 true,就重复执行
      {
      // 循环体代码
      // 必须包含迭代语句,否则会导致死循环
      }
      // 核心规则:while 后面不能有分号 (;)
  2. 循环的三个关键要素

    • 初始化 (Initialization):在循环开始前设置初始值。
    • 条件 (Condition)while 括号内的布尔表达式,决定循环是否继续。
    • 迭代 (Iteration):在循环体内,修改控制变量的值 (如 count++),使其最终能达到结束条件。
  3. 陷阱:死循环 (Infinite Loop)

    • 如果循环体代码没有修改控制变量,条件将永远为 true,程序将永远执行下去。

第十五课课堂作业

  1. 初始化变量

    • 声明一个 int 变量 currentYear,赋值为 2015
  2. 创建 while 循环

    • 编写一个 while 循环,让程序输出从 20152020 的所有年份
    • 条件:循环应该在 currentYear 小于或等于 2020 时继续。
    • 迭代:在循环体内,使用您学过的递增运算符 (++) 将 currentYear 每次增加 1。
  3. 输出结果:在循环体内输出当前的 currentYear 值,并在循环结束后输出一行 "完成!"


🔁 第 16 课:专业的计数循环——for 循环

📚 核心知识点

for 循环是 C# 中最常用、最强大的循环结构,适用于已知循环次数或需要精确控制计数器时。它将循环的三个要素集成在一行中。

  1. 基本结构

    • 语法:三个表达式必须用分号 ; 分隔。
      1
      2
      3
      4
      5
      for (初始化表达式; 条件表达式; 迭代表达式)
      {
      // 循环体:需要重复执行的代码
      }
      // 注意:for 后面不能有分号 (;)
  2. 三个表达式的作用

    • 初始化 (int i = 0):在循环开始前,只执行一次,设置计数器初始值。
    • 条件 (i < N):每次循环开始前检查,决定是否继续循环。结果为 true 则继续。
    • 迭代 (i++):在每次循环体执行完毕后执行,用于修改计数器(递增或递减),防止死循环。
  3. forwhile 的区别

    • for 结构更紧凑,将三要素集中管理,适用于固定次数计数。
    • while 结构更灵活,适用于依赖复杂条件而非计数器的场景。
  4. 循环流程 (以递增为例)

      1. 初始化
      1. 检查条件
      1. (若条件为真) 执行循环体
      1. 执行迭代表达式
      1. 回到第 2 步检查条件… 直到条件为假。

第十六课课堂作业

🎯 课堂作业目标

使用 for 循环创建一个倒数计数器。

📄 题目要求

  1. 创建 for 循环
    • 编写一个 for 循环,命名计数器变量为 j
    • 初始化:让 j10 开始 (int j = 10)。
    • 条件:循环应该在 j 大于等于 5 时继续 (j >= 5)。
    • 迭代:在每次循环后,使用递减运算符 (--) 将 j 减少 1 (j--)。
  2. 输出结果:在循环体内输出当前的 j 值,并在循环结束后输出一行 "倒数完成!"

🔁 第 17 课:至少执行一次的循环——do-while 循环

🎯 核心概念:先执行,后判断

do-while 循环是 C# 中唯一一个保证循环体内的代码至少会执行一次的循环。

1. do-while 循环的基本结构

它的结构将判断条件放在了循环体的末尾

1
2
3
4
5
6
7
do
{
// 循环体:这里的代码至少会被执行一次。
// 即使 while 括号内的条件一开始就是 false,也会先执行一次这里的代码。

// 迭代语句,用于修改循环控制变量
} while (布尔表达式); // **重要!while 语句末尾必须有分号 ;**

do 关键字:表示开始执行循环体。 {} 代码块:循环体,先执行。 while (布尔表达式);:在循环体执行完之后才检查条件。

2. do-while 的典型应用场景

do-while 最常用于需要强制用户交互的场景,例如:

要求用户输入密码,直到密码正确为止。

在程序末尾询问用户是否要继续或退出(“你确定退出吗?(Y/N)”)。

读取文件或网络数据流时,至少要尝试读取一次。

示例:演示至少执行一次

1
2
3
4
5
6
7
int i = 10; 
// 保证输出 "i 的值:10",即使条件为 false
do
{
Console.WriteLine("i 的值:" + i); // 输出一次
i++; // i 变为 11
} while (i < 5); // 此时条件 (11 < 5) 为 False,循环结束
循环类型 类型检查时机 保证执行次数 语法陷阱(分号)
while 循环开始前 可能为 0 次 结尾不能有分号 ;
for 循环开始前 可能为 0 次 结尾不能有分号 ;
do-while 循环结束后 至少 1 次 while 结尾必须有分号 ;

第十七课课堂作业

🎯 课堂作业目标

使用 do-while 循环展示其“至少执行一次”的特性。

📄 题目要求

  1. 初始化变量
    • 声明一个 string 变量 userInput,赋值为 "继续"
  2. 创建 do-while 循环
    • 编写一个 do-while 循环,在 do 块内将 userInput 的值设置为 "退出",并输出该值。
    • 条件:循环应该在 userInput 等于 "继续" 时继续。
  3. 核心要求
    • 确保 do-while 结构末尾的 while 语句后必须有分号 ;
    • 正确使用关系运算符 == 进行比较。

🛑 第 18 课:控制循环流——break 和 continue

📚 核心知识点

📖 概念 (Concept)

break 关键字是 C# 中用于立即终止(即停止)它所在的最内层循环(包括 for, while, 或 do-while 循环)的控制流语句。

  • 一旦程序执行到 break 语句,无论循环条件是否仍为真(true),循环都会立刻停止执行。
  • 程序流程将跳转到该循环结构之后的下一条可执行语句。

📝 语法和用途 (Syntax and Usage)

break 通常与条件判断(if 语句)结合使用,用于实现“提早退出”或“找到目标后停止”的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 示例:在找到目标数字 77 后立即停止搜索
int[] dataSet = { 10, 35, 77, 88, 99 };
int target = 77;
bool isFound = false;

for (int i = 0; i < dataSet.Length; i++)
{
Console.WriteLine($"正在检查索引 {i},数值:{dataSet[i]}");

if (dataSet[i] == target)
{
isFound = true;
Console.WriteLine($"\n目标 {target} 已找到!");
break; // 👈 紧急停止整个 for 循环
}
}

if (!isFound)
{
Console.WriteLine("未找到目标。");
}
// 注意:如果 dataSet[2] == 77 触发 break,索引 3 和 4 的数据将不会被检查。

关键点总结

完全退出: break 会导致程序完全退出当前的循环结构。

嵌套循环: 在多层嵌套循环中,break 只影响它所在的最内层循环。

⏭️ 2. continue 关键字:跳过本次迭代

📖 概念 (Concept)

continue 关键字用于跳过当前循环体中剩余的代码,并立即开始下一次迭代。

当程序执行到 continue 语句时,它会忽略 continue 之后、当前循环体末尾之前的所有代码。

对于 for 循环,程序会跳转到迭代语句(如 i++)执行。

对于 while 或 do-while 循环,程序会跳转到循环的条件判断处。

📝 语法和用途 (Syntax and Usage)

continue 常用于数据筛选或处理异常情况,使得循环只处理符合特定条件的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 示例:计算 1 到 5 之间所有奇数的总和,跳过偶数
int totalSum = 0;

for (int i = 1; i <= 5; i++)
{
// 如果 i 是偶数(i 除以 2 余 0),则跳过本次循环
if (i % 2 == 0)
{
Console.WriteLine($"i={i} 是偶数,执行 continue 跳过累加。");
continue; // 👈 跳过本次循环的剩余代码
}

// 只有 i 是奇数时,这行代码才会被执行
totalSum += i;
Console.WriteLine($"i={i} 是奇数,加入总和。当前总和:{totalSum}");
}

Console.WriteLine("最终奇数总和:" + totalSum); // 结果为 1 + 3 + 5 = 9

关键点总结

  • 跳过部分:continue 只跳过当前这一次迭代中 continue 之后的代码。
  • 循环继续:循环本身不会停止,下一轮迭代会照常进行。

3. break 和 continue 的区别对比

特性 break continue
行为 立即终止整个循环 立即跳过本次循环,进入下一轮迭代
循环状态 循环被彻底退出 循环继续运行,只是当前步骤被跳过
主要用途 提早退出,停止搜索/处理 筛选数据,跳过不合格的迭代

第十八课课堂作业

🎯 课堂作业目标

请编写一个 for 循环,计数器 i 从 1 循环到 10,同时使用 break 和 continue 来控制流程,并确保最终只输出未被跳过的数字:

要求说明:

  • 循环范围: i 从 1 开始,到 10 结束(包含 10)
  • 筛选条件 (continue): 如果 i 能被 3 整除(即 i % 3 == 0),则跳过本次循环
  • 终止条件 (break): 如果 i 大于 7,立即退出整个循环
  • 预期输出: 1, 2, 4, 5, 7

请将完成的 C# 代码提交给我。


🔄 第 19 课:嵌套循环

📚 核心知识点

嵌套循环就是在一个循环里面再放一个循环,就像俄罗斯套娃一样,一个套一个。这个技能特别适合处理表格数据,比如成绩单、日历、棋盘格子这些有行有列的东西。

📖 概念 (Concept)

嵌套循环就是让程序"一层一层"地干活。外面的循环负责大方向,里面的循环负责具体细节。

  • 外层循环:就像楼层导航,告诉你现在在第几层
  • 内层循环:就像每层的房间号,告诉你这个层里有哪些房间

📝 语法和用途

嵌套循环最常用的地方就是处理那些有行有列的数据结构。

1
2
3
4
5
6
7
8
9
// 示例:打印 3x3 的乘法表,就像小学生背的九九乘法表
for (int i = 1; i <= 3; i++) // 外层循环:负责行(第几行)
{
for (int j = 1; j <= 3; j++) // 内层循环:负责列(第几列)
{
Console.Write($"{i} × {j} = {i * j}\t"); // 打印乘法结果
}
Console.WriteLine(); // 一行结束了,换行
}

实际应用:二维数组遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 二维数组就像一个真实的表格,有行有列
int[,] matrix =
{
{ 1, 2, 3 }, // 第一行
{ 4, 5, 6 }, // 第二行
{ 7, 8, 9 } // 第三行
};

// 遍历二维数组,就像按顺序看表格里的每个格子
for (int row = 0; row < matrix.GetLength(0); row++) // 行循环
{
for (int col = 0; col < matrix.GetLength(1); col++) // 列循环
{
Console.Write($"[{row},{col}] = {matrix[row,col]} "); // 显示格子内容
}
Console.WriteLine(); // 这一行看完了,换下一行
}

关键点总结

  • 执行次数:如果外层循环跑 m 次,内层循环跑 n 次,总共要跑 m × n 次(工作量会爆炸式增长!)
  • 循环变量:内外层的计数器变量名不能一样,不然会搞混
  • 性能考虑:嵌套循环很耗性能,就像让一个人干很多重复活,要小心使用

核心概念

  • 嵌套循环:就像打扫一栋楼,外层循环负责楼层(1楼、2楼、3楼),内层循环负责每层的房间(101、102、103…)
  • 外层循环:大管家,决定现在处理哪一层
  • 内层循环:小工,负责把这一层的所有房间都打扫一遍
  • 执行次数:如果一栋楼有3层,每层有4个房间,总共要打扫 3 × 4 = 12 个房间

🎯 实践作业

创建一个 4x4 的二维数组,使用嵌套循环:

  1. 填充数组内容(可以是乘法表或其他规律数据)
  2. 使用嵌套循环打印数组内容
  3. 计算并显示数组对角线元素的和

📚 第 20 课:数组和 foreach 有啥用?

💡 数组(Arrays):管理大量同类数据的"盒子"

想象一下,你是个超市老板,要记录店里100种商品的价格。

不用数组:你得创建 price1, price2, price3… 一直到 price100,这简直是要疯了!

用数组:你只需要一个叫 prices 的"盒子",然后说:"给我一个能装100个价格的盒子"就行了。

数组就是让你把一堆同类型的东西(全是数字、全是字符串等)放进一个带编号的大盒子里,用一个名字管着所有东西。

基本概念与特性

同类型:盒子里只能放同一种东西,要么全是数字,要么全是文字,不能混着放。

固定大小:盒子买来多大就是多大,装满了想装更多就得换个新盒子。

编号访问:每个格子都有编号,从0开始数,第1个格子编号是0,第2个是1。

传统方式的痛苦

没有数组的时候,管理大量数据就像噩梦:

1
2
3
4
5
6
// 传统方式:每个数据都要单独起名字
double price1 = 19.99;
double price2 = 29.50;
double price3 = 15.75;
// 想象一下如果有100个价格... 😱
double total = price1 + price2 + price3; // 加起来要写一堆变量名

数组的爽快体验

数组让你用一个名字管住所有数据:

1
2
3
4
5
6
7
8
9
10
11
12
// 数组方式:一个名字管所有
double[] prices = { 19.99, 29.50, 15.75, 8.99, 12.50 }; // 一行搞定5个价格

// 想看第3个价格?(记住从0开始数)
Console.WriteLine($"第三个商品价格是:{prices[2]}"); // 输出:15.75

// 想改第1个价格?
prices[0] = 25.99; // 涨价了!
Console.WriteLine($"第一个商品新价格:{prices[0]}");

// 想知道一共有多少个商品?
Console.WriteLine($"总共{prices.Length}个商品"); // Length告诉你数量

数组的小脾气

数组大小固定,这是它的优点也是缺点:

1
2
3
4
5
6
// 盒子大小一旦定死就不能改
string[] students = new string[3]; // 只能装3个学生
students[0] = "小明";
students[1] = "小红";
students[2] = "小华";
// students[3] = "小李"; // 错误!越界了,程序会崩溃!💥

核心概念

  • 数组:就像一个有很多格子的抽屉,每个格子都有编号,只能放同类型的东西
  • 索引:格子的编号,从0开始,第1个格子编号是0,第2个是1
  • 固定大小:抽屉格子数量确定后就不能变,想放更多东西只能换个新抽屉

💡 foreach 循环:数组的最佳"检查员"

现在你有一个装满100个价格的盒子(数组),你的任务是:把这100个价格全部加起来算总额。

用 for 循环:你需要一个计数器 i,从0开始数到99,每次写 total += prices[i],还得时刻小心别数过头了。

用 foreach 循环:这个循环就是专门为数组设计的"自动检查员"。

1
2
3
4
5
6
7
8
9
10
// 传统 for 循环 - 手动数数,容易出错 🤯
double[] prices = { 19.99, 29.50, 15.75, 8.99, 12.50 };
double total = 0;

for (int i = 0; i < prices.Length; i++) // 记住要 < Length,不是 <=
{
total += prices[i];
Console.WriteLine($"第{i + 1}个商品价格:{prices[i]},累计:{total}");
}
// 如果写成 i <= prices.Length,就会出错!💥

用 foreach 循环:这个循环就是为数组和集合量身定做的"自动检查员"。

1
2
3
4
5
6
7
8
9
10
// foreach 循环 - 自动帮你数,超级安全!😎
double[] prices = { 19.99, 29.50, 15.75, 8.99, 12.50 };
double total = 0;

foreach (double price in prices) // price 会自动拿到数组里的每个值
{
total += price;
Console.WriteLine($"当前商品价格:{price},累计:{total}");
}
// 完全不用担心索引问题,foreach 会帮你搞定一切!

foreach 循环的口头禅是:“嘿,你给我一个数组,我保证从头到尾,挨个把里面的东西拿出来给你用一遍,你不用管我在第几个,我帮你数!”

它的好处:**安全、简单、不容易出错。**你只需要声明一个临时变量(比如叫 price),循环就会自动把数组里的每个元素赋值给 price,你只需要处理 price 就行。

它的限制:它只能读取数据。如果你想把数组里的每个价格都涨 1 块钱(修改数据),foreach 就不行了,你还是得回到老老实实的 for 循环,因为它需要精确地知道位置(索引)才能修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
// ❌ foreach 不能修改数组内容
int[] scores = { 80, 90, 85 };
foreach (int score in scores)
{
// score = score + 5; // 这行代码不会修改原数组!只是修改了临时变量
}

// ✅ for 循环可以修改数组内容
for (int i = 0; i < scores.Length; i++)
{
scores[i] = scores[i] + 5; // 这行代码会真正修改数组
}
// 现在 scores 变成了 { 85, 95, 90 }

简单来说:数组用来存,foreach 循环用来读。

核心概念

  • foreach:就像一个自动计数员,你告诉他检查哪个抽屉,他会从第一个格子开始,挨个把里面的东西拿出来给你看,数完了就自动停止
  • 迭代变量:每次循环时,foreach会把数组中当前格子的东西复制一份给你,你拿到的是复印件,不是原件
  • 只读访问:你只能看复印件上的内容,不能通过修改复印件来改变原件的内容

🎯 更多实用示例

示例1:统计学生成绩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 班级里有5个学生的成绩,放在一个数组里
int[] grades = { 85, 92, 78, 96, 88 };

// ===== 第一部分:计算平均分 =====
// 用 foreach 计算平均分 - 这是最安全的方法
int sum = 0; // 先准备一个变量来存总分,初始值为0

// foreach 会自动把数组里的每个成绩拿出来,依次赋值给 grade 变量
// 第一次循环:grade = 85,sum = 0 + 85 = 85
// 第二次循环:grade = 92,sum = 85 + 92 = 177
// 第三次循环:grade = 78,sum = 177 + 78 = 255
// 第四次循环:grade = 96,sum = 255 + 96 = 351
// 第五次循环:grade = 88,sum = 351 + 88 = 439
foreach (int grade in grades)
{
sum += grade; // 等同于 sum = sum + grade,把当前成绩加到总分里
}

// 计算平均分:总分 ÷ 人数
// 注意:(double)sum 是把整数转成小数,这样除法结果才是小数
// 如果不转,85+92+78+96+88=439,439÷5=87(整数除法会丢掉小数部分)
// 转成double后,439.0÷5=87.8(保留小数)
double average = (double)sum / grades.Length; // grades.Length = 5(数组长度)
Console.WriteLine($"班级平均分:{average:F1}"); // :F1 表示保留1位小数,输出87.8

// ===== 第二部分:找出最高分 =====
// 用 foreach 找出最高分 - 体现了"打擂台"的思路
int maxGrade = grades[0]; // 先假设第一个成绩是最高分,maxGrade = 85

// 再次遍历数组,比较每个成绩和当前最高分
// 第一次循环:grade = 85,85 > 85?不成立,maxGrade 还是 85
// 第二次循环:grade = 92,92 > 85?成立,maxGrade 更新为 92
// 第三次循环:grade = 78,78 > 92?不成立,maxGrade 还是 92
// 第四次循环:grade = 96,96 > 92?成立,maxGrade 更新为 96
// 第五次循环:grade = 88,88 > 96?不成立,maxGrade 还是 96
foreach (int grade in grades)
{
if (grade > maxGrade) // 如果当前成绩比已知的最高分还高
{
maxGrade = grade; // 那么更新最高分记录
}
}
Console.WriteLine($"最高分:{maxGrade}"); // 最终输出96

示例2:处理购物清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 购物清单:商品名称和数量
// 注意:这里用了两个数组,通过相同的索引来关联数据
// items[0]="苹果" 对应 quantities[0]=3
// items[1]="香蕉" 对应 quantities[1]=2
// 以此类推...
string[] items = { "苹果", "香蕉", "橙子", "葡萄" };
int[] quantities = { 3, 2, 5, 1 };

Console.WriteLine("=== 我的购物清单 ===");

// ===== 第一部分:显示详细清单 =====
// 这里必须用 for 循环,因为需要同时访问两个数组的相同位置
// foreach 无法做到这一点,因为它不知道当前是第几个元素
for (int i = 0; i < items.Length; i++) // i 从 0 循环到 3
{
// 第一次循环:i=0,显示 items[0]="苹果" 和 quantities[0]=3
// 第二次循环:i=1,显示 items[1]="香蕉" 和 quantities[1]=2
// 第三次循环:i=2,显示 items[2]="橙子" 和 quantities[2]=5
// 第四次循环:i=3,显示 items[3]="葡萄" 和 quantities[3]=1
Console.WriteLine($"{items[i]}{quantities[i]}个");
}

// ===== 第二部分:计算总数量 =====
// 用 foreach 计算总数量 - 只需要 quantities 数组,用 foreach 最安全
int totalItems = 0; // 准备变量存总数量,初始值为0

// foreach 会自动遍历 quantities 数组的每个元素
// 第一次循环:qty = 3,totalItems = 0 + 3 = 3
// 第二次循环:qty = 2,totalItems = 3 + 2 = 5
// 第三次循环:qty = 5,totalItems = 5 + 5 = 10
// 第四次循环:qty = 1,totalItems = 10 + 1 = 11
foreach (int qty in quantities) // qty 是临时变量,每次拿到一个数量值
{
totalItems += qty; // 把当前数量加到总数量里
}
Console.WriteLine($"总共买了{totalItems}个水果"); // 输出11

// ===== 知识点总结 =====
// 1. 什么时候用 for 循环:需要知道当前是第几个元素时(比如要同时访问多个关联数组)
// 2. 什么时候用 foreach 循环:只需要遍历所有元素,不需要知道位置时(比如求和、计数)
// 3. 两个数组关联:通过相同的索引来保持数据对应关系
// 4. 变量命名:用有意义的名字让代码更容易理解(如 qty 而不是 x)

第二十课课堂作业

现在,请你用上刚刚学的数组和 foreach 知识来完成下面的任务:

🎯 任务要求

  1. 创建数组:声明一个名为 monthlySales 的 decimal(精确的小数类型,适合金额或销量)数组,存储至少 5 个月的销售额数据(随便写 5 个小数就行,比如:{ 12500.50m, 8900.75m, 15000.00m, 9500.20m, 18000.30m })。记得,decimal 后要加个 m。

  2. 遍历计算

    • 初始化一个 decimal 变量 annualTotal 为 0。
    • 使用 foreach 循环遍历 monthlySales 数组,把所有的销售额累加到 annualTotal 中。
  3. 输出结果

    • 打印出数组的总销售额(annualTotal)。
    • 打印出数组中月份的数量(使用 .Length 属性)。

请提交您的 C# 代码!


🎨 第 21 课:美化与开发你的 Console 项目

0. 声明:附加课的重要意义

在您之后的学习中,您会发现 Console(控制台)程序虽然简单,却是学习 C# 核心逻辑和算法的最佳场所。掌握这些美化技巧,能让您在调试和开发小型工具时更加高效和舒适。


1. 颜色输出:让文字"活"起来

让 Console(控制台)输出有颜色,就像给您的程序穿上了衣服。我们主要通过 Console.ForegroundColor 属性来实现。

1.1 同行内显示相同颜色

这是最简单的应用。您只需要设置一次颜色,然后打印。

1
2
3
4
5
6
7
8
9
// 1. 设置前景颜色为绿色
Console.ForegroundColor = ConsoleColor.Green;

// 2. 打印一行文本。
// 注意:这行文本会全部是绿色。
Console.WriteLine("【操作成功】数据处理已完成。");

// 3. 恢复默认颜色!非常重要!
Console.ResetColor();

1.2 同行内显示不同颜色(简单实现版)

如果您想在同一行里,让一部分字是绿色,一部分字是白色,怎么办?

您必须在不同颜色的输出之间,手动切换和重置颜色。

1
2
3
4
5
6
7
8
9
// 目标:打印绿色的 [ OK ],紧接着打印白色的 任务已启动。

// 1. 打印绿色部分
Console.ForegroundColor = ConsoleColor.Green; // (控制台)设置颜色为绿色
Console.Write("[ OK ] "); // (控制台)注意这里要用 Write,不要换行

// 2. 恢复默认颜色,打印白色部分
Console.ResetColor(); // (控制台)立即恢复默认颜色(通常是白色)
Console.WriteLine("任务已启动。"); // (控制台)打印剩余部分,并换行

工作原理Console.Write 不换行,所以两个输出是连在一起的,但我们通过中间的 Console.ResetColor() 切换了颜色。

1.3 同行内显示不同颜色(提升代码可读性 - 方法封装)

如果有很多地方需要打印彩色文字,每次都写 Console.ForegroundColor = ...Console.ResetColor() 会非常麻烦,代码也会变得很乱。

我们把这个过程"封装"成一个方法(Function)。

第一步:为什么需要封装?

方法(或函数)就像一个工具箱里的工具。每次你需要钉钉子,你只需要拿起锤子(调用方法),而不需要自己发明一把锤子。封装就是把重复的步骤打包。

第二步:创建工具(封装方法)

虽然我们还没学到方法(Methods)的正式用法,但您可以先简单理解并使用这个工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在 Main 函数外面,创建一个新的工具,名字叫 PrintColor
// 它接收两个参数:要打印的文本 (message) 和想要的颜色 (color)
public static void PrintColor(string message, ConsoleColor color)
{
// 1. 保存当前颜色,以便稍后恢复
ConsoleColor originalColor = Console.ForegroundColor;

// 2. 设置用户想要的颜色
Console.ForegroundColor = color;

// 3. 打印文本
Console.Write(message);

// 4. 恢复成原来的颜色,避免影响后面的代码
Console.ForegroundColor = originalColor;
}

第三步:使用工具(调用方法)

现在,打印彩色文字就变得非常简单和清晰了:

1
2
3
4
5
6
7
// 目标:打印 黄色的 [ 警告 ],紧接着打印 白色的 配置错误。

// 打印黄色的 [ 警告 ]
PrintColor("[ 警告 ] ", ConsoleColor.Yellow);

// 打印默认颜色的 配置错误
Console.WriteLine("配置错误。");

优势:无论您想用什么颜色,现在只需要一行 PrintColor(...) 就能实现,代码干净多了!


2. 同行输出图案:进度条的奥秘

在 Console(控制台)里做动画(比如进度条)的核心秘密就是:重复打印和覆盖。

2.1 理论:如何让文字"动"起来?

Console(控制台)不像图形界面,它只能一行一行往下写。要实现进度条在同一行更新,我们必须利用光标控制:

  • Console.Write:在当前光标位置打印。
  • Console.SetCursorPosition(列, 行):将光标移回你想要修改的位置。

进度条的流程

  1. 打印 [----------]
  2. 将光标移回到 [ 的后面。
  3. 打印一个 >(作为进度),再打印空格,覆盖掉旧的内容。
  4. 暂停一下(Thread.Sleep)。
  5. 重复步骤 2、3、4。

2.2 实践:简单进度条(示例代码)

这段代码展示了如何使用光标定位来制作一个 10 步的进度条。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.Threading; // 需要引入这个命名空间来使用 Sleep

public static void MakeProgressBar()
{
// 1. 设置光标位置:我们将进度条放在第 5 行,第 0 列
int startRow = 5;
Console.SetCursorPosition(0, startRow);
Console.Write("进度:[ ]"); // (控制台) 打印背景,长度 10 个空格

// 2. 开始循环,模拟 10 步进度
for (int i = 0; i <= 10; i++)
{
// 将光标移回到进度条的起始位置 (0 列 + 5 个字符宽度的 "进度:[")
Console.SetCursorPosition(6, startRow);

// 打印 i 个 #,代表进度
Console.Write(new string('#', i));

// 打印 (10 - i) 个空格,确保覆盖掉之前可能留下的字符
Console.Write(new string(' ', 10 - i));

// 打印百分比,移到进度条最右边
Console.SetCursorPosition(17, startRow);
Console.Write($"{i * 10}%");

// 暂停 300 毫秒,让进度看起来是"动"起来的
Thread.Sleep(300);
}
}
// 在您的 Main 函数里调用 MakeProgressBar(); 即可看到效果

3. TUI 界面制作:让你的程序像个工具

3.1 什么是 TUI 界面?有什么用?

TUI (Text-User Interface):文本用户界面,就是用文字、符号和颜色在 Console(控制台)里搭出来的界面。

**有什么用?**很多专业的工具(比如 Linux 上的配置工具,或者 C# 的单元测试报告)都会用 TUI,因为它轻量、高效,而且比纯文本输出(一行接一行)更有交互感和结构感。

3.2 开始熟悉 TUI 界面

TUI 界面的第一步就是清除和定位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Console.Clear(); // (控制台) 清空屏幕,获得一块干净的画布
Console.Title = "文件传输工具 V1.0"; // (控制台) 设置窗口标题

// 打印一个边框或者固定信息
PrintColor("========================================\n", ConsoleColor.Cyan);
Console.WriteLine(" 文件传输服务 | 状态监控中心");
PrintColor("========================================\n", ConsoleColor.Cyan);

// 使用光标定位在 (1, 5) 打印状态信息
Console.SetCursorPosition(1, 5);
Console.Write("任务名称:");
Console.SetCursorPosition(15, 5);
PrintColor("[ 等待中 ]", ConsoleColor.Yellow);

// ...

3.3 控制 Console 界面大小

如果用户调整了窗口大小,您的 TUI 界面可能会乱。您可以设置 Console(控制台)的固定大小。

1
2
3
// 限制窗口最小大小,避免内容被截断
Console.SetWindowSize(80, 25); // (控制台) 设置窗口宽度为 80,高度为 25 行
Console.SetBufferSize(80, 25); // (控制台) 缓冲区大小也一致,防止用户上下滚动

3.4 使用快捷键控制 Console

如何让用户按下一个键就执行一个操作?比如按 ‘Q’ 退出。

我们使用 Console.ReadKey(true) 来读取用户按下的单个键,并且 true 表示不把按键显示在屏幕上(这样界面就不会被按键污染)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Console.WriteLine("\n\n请按 Q 键退出,或按 P 键开始...");

ConsoleKeyInfo keyInfo = Console.ReadKey(true); // (控制台) 读取按键,但不显示

// 判断用户按了什么键
if (keyInfo.Key == ConsoleKey.Q)
{
Console.WriteLine("程序已关闭。");
// 这里通常会写 return; 来结束 Main 函数
}
else if (keyInfo.Key == ConsoleKey.P)
{
Console.WriteLine("任务开始执行...");
}

3.5 将第二个内容加以应用(进度条)

在 3.2 的 TUI 示例中,您可以将进度条(2.2)放置在一个固定的位置(比如第 10 行),这样进度条在更新时,屏幕上方的固定信息就不会被干扰。

3.6 如何规范设计 TUI 程序?

最简单也是最重要的方法就是:分块封装!

您不应该把所有的 Console.WriteSetCursorPosition 都塞进 Main 函数里。

  • 标题/边框:封装成 DrawHeader() 方法。
  • 状态信息:封装成 UpdateStatus(int row, string status) 方法。
  • 进度条:封装成 UpdateProgressBar(int percentage) 方法。

这样,如果老板说:“把进度条改成蓝色”,您只需要去 UpdateProgressBar 方法里改一个颜色,而不用翻遍整个程序。


4. 跨平台 Console 开发:现代 C# 的优势

4.1 Linux & macOS Console 开发

在以前的 C# (.NET Framework) 中,Console 程序通常只能在 Windows 上运行。但现在,C# 基于 .NET (Core) 平台,您的 Console 程序天生就是跨平台的!

您在 Windows 上写的代码,可以直接在 Linux 或 macOS 上通过 dotnet run 命令运行。

好处:您不需要重新学习新的语言或工具,就可以在任何主流操作系统上开发命令行工具。

4.2 和 Windows Console 软件的区别

虽然代码是跨平台的,但 Console 的外观和行为在不同系统上会有细微差别:

  • 颜色:Windows Console 的颜色和 Linux 终端(Terminal)的颜色显示效果可能略有不同。
  • 光标控制:极少数复杂的光标控制(例如调整字体)可能在 Linux/macOS 终端中不支持或行为不同,但我们刚才学到的 SetCursorPositionForegroundColor 在绝大多数现代终端上都是兼容的。

🎯 附加课总结

通过这节附加课的学习,你已经掌握了:

  1. 颜色输出 - 学会了使用 Console.ForegroundColorPrintColor 方法
  2. 进度条制作 - 理解了光标定位和重复覆盖的原理
  3. TUI 界面设计 - 掌握了文本用户界面的基本构建方法
  4. 快捷键控制 - 学会了使用 Console.ReadKey 响应用户输入
  5. 跨平台开发 - 了解了现代 C# 的跨平台优势

这些技能将让你的 Console 程序从简单的命令行工具升级为专业的终端应用!


💻 第 22 课:程序启动的秘密——如何给 Main 函数传递参数?

💡 核心目标栏目:命令行参数可以很好地解决程序提问的问题!

想象一下,我们现在正在编写一个简单的回显程序(Echo Program),它的工作就是根据你的指令,把一段文字用你指定的颜色打印出来。如果您不知道如何使用命令行参数,您的程序可能会像一个"只会主动提问的笨蛋软件,特傻。":

程序启动: (一个黑色的控制台框弹了出来)
程序: “您好!请问您要我显示的内容是什么呀?”
用户: (输入) “锁那!”
程序: “好的!那您希望这段内容用什么颜色显示呢?”
用户: (输入) “yellow”
程序: “好的,已记录。正在显示…”

这种程序效率极低,每次启动都要问一遍。命令行参数就是解决这个问题的钥匙!它让您的程序变得更聪明。它允许您一次性把所有的要求都交给程序,让程序立即执行,无需提问:

1
MyEcho.exe yellow "锁那!"

程序启动,立刻知道内容是"锁那!“,颜色是"yellow”,然后直接用黄色打印出来!

我们今天就来学习如何让程序具备这种"接收指令"的能力!


第一步:认识 Main 函数的秘密——string[] args

一切都始于您的程序入口函数 Main:

1
2
3
4
public static void Main(string[] args)
{
// ... 您的程序代码
}

我们要看的就是这个括号里面的内容:string[] args

1. string[] args 是什么?

它是一个"指令包裹":当您在程序名后面输入任何东西,操作系统就会把它们收集起来,打包成一个包裹,然后传递给 Main 函数。这个包裹,就是 string[] args

包裹里装的是什么? 里面装的都是字符串(文本)。

怎么区分包裹里的内容? 靠空格!您每按一个空格,包裹里就会多一个独立的字符串。

您在 CMD 中输入 args 包裹里有什么? 数组长度 args.Length
MyEcho.exe [] (空包裹) 0
MyEcho.exe red [“red”] 1
MyEcho.exe red “Hello” [“red”, “Hello”] 2

2. 记住:双引号 “” 的用法

如果您的消息内容本身含有空格,比如"你好 世界",您必须用英文双引号 “” 把它括起来,否则操作系统会把它分成两个参数!

输入:MyEcho.exe red "你好 世界" → 数组长度是 2。
输入:MyEcho.exe red 你好 世界 → 数组长度是 3。


第二步:如何将指令传递给程序?(两种操作环境)

您必须学会如何在不同的环境下,将参数"喂"给程序。

2.1 在 Visual Studio (VS) 里模拟输入指令(调试阶段)

这是最方便的调试方法。您无需每次都打开命令行。请您跟着以下步骤操作:

  1. 右键点击项目: 在 Visual Studio 的"解决方案资源管理器"中,右键点击您的项目名称,选择"属性"(Properties)。
  2. 找到调试设置: 在左侧导航栏中,点击"调试"(Debug) 选项卡。
  3. 找到输入框: 找到标有"应用程序参数"(Application Arguments) 的文本框。
  4. 输入指令: 在这个框里,输入您想要测试的参数,例如:yellow "锁那!"
  5. 运行: 此时,您直接按 F5 运行程序,VS 会自动把这些内容打包成 args 数组并传递给 Main 函数。

2.2 在 CMD 或终端里输入指令(实际运行阶段)

这是程序发布后,用户运行的方式。

  1. 找到程序: 找到您的程序生成的可执行文件(.exe)。
  2. 打开终端: 打开 CMD 或 PowerShell。
  3. 运行并传参: 输入程序名,后面加上您的指令。
1
MyEcho.exe green "一切就绪"

第三步:实践——制作最简单的参数软件 (Echo Program)

现在,我们用最简单的方式,来制作这个 Echo 程序。我们将只用最基础的 if 和 else if 语句来检查参数数量。

步骤 3.1:程序的骨架与默认设置

我们先写出 Main 函数的骨架,并定义一个默认的消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;

public class EchoProgram
{
public static void Main(string[] args)
{
// 告诉用户程序收到了多少指令包裹
Console.WriteLine($"程序启动!收到了 {args.Length} 个参数包裹。");
Console.WriteLine("--------------------------------------");

// 接下来我们用 if 语句判断包裹里有多少东西:
// ...
}
}

步骤 3.2:处理零参数(args.Length == 0)

如果用户什么都没输入,我们至少应该告诉他如何使用程序。

1
2
3
4
5
6
7
8
9
if (args.Length == 0)
{
// 情况 A:没有参数 (空包裹)

Console.ForegroundColor = ConsoleColor.Red; // (控制台) 设置红色
Console.WriteLine("[ 错误 ] 缺少指令!请在程序名后输入指令。");
Console.WriteLine("用法示例:MyEcho.exe yellow \"你的消息\"");
Console.ResetColor(); // (控制台) 恢复默认颜色
}

步骤 3.3:处理两参数(args.Length == 2)——成功的彩色回显

这是程序能成功显示彩色回显的理想情况。我们直接用最简单的 if 语句来检测颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
else if (args.Length == 2)
{
// 情况 B:收到 2 个参数 (颜色 + 消息)

// 我们约定:args[0] 是颜色,args[1] 是消息
string colorName = args[0];
string message = args[1];

// 简单的 if 语句来检测颜色
if (colorName == "yellow")
{
Console.ForegroundColor = ConsoleColor.Yellow; // (控制台) 设置黄色
Console.WriteLine($"成功回显消息: {message}");
Console.ResetColor(); // (控制台) 恢复默认颜色
}
else if (colorName == "red")
{
Console.ForegroundColor = ConsoleColor.Red; // (控制台) 设置红色
Console.WriteLine($"成功回显消息: {message}");
Console.ResetColor(); // (控制台) 恢复默认颜色
}
else
{
// 如果输入的颜色不是 yellow 或 red,我们仍然打印消息,但用默认色。
Console.WriteLine($"[ 警告 ] 颜色 {colorName} 不支持,使用默认色回显消息: {message}");
}
}

步骤 3.4:处理单参数(args.Length == 1)——避免崩溃的关键

这是最简单但最重要的一步,它让您的程序不会因为少输入一个参数而崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        else if (args.Length == 1)
{
// 情况 C:收到 1 个参数

// 我们约定:如果只有一个参数,我们认为它就是用户想显示的消息。
string message = args[0];

// 打印消息,使用默认颜色。
Console.WriteLine($"接收到 1 个参数,视为消息,用默认色回显: {message}");

// 解决了您的疑问:
// 即使用户输入 MyEcho.exe yellow,它也会进入这个块,
// 打印 "接收到 1 个参数...回显: yellow",不会崩溃!
}
else // args.Length > 2 的情况
{
// 简单处理:提示忽略多余的参数
Console.WriteLine($"[ 警告 ] 参数过多 ({args.Length} 个),只处理了前 2 个参数。");
}

// 最终总结
Console.WriteLine("--------------------------------------");
Console.WriteLine("程序执行完毕。");

Console.ReadKey(); // (控制台) 等待按键
}
}

🎯 最终代码整合与测试

将上面的所有代码块(Main 函数中的所有 if 和 else if 语句)整合在一起,就得到了一个完整的、简单参数检测程序。

再次测试您的要求:

  • 输入:MyEcho.exe yellow "锁那!" → 进入 args.Length == 2 → 输出:黄色的 “成功回显消息: 锁那!” ✓ 正确
  • 输入:MyEcho.exe "锁那!" → 进入 args.Length == 1 → 输出:默认色的 “回显: 锁那!” ✓ 正确
  • 输入:MyEcho.exe testcolor "消息" → 进入 args.Length == 2 → 颜色不是 yellow/red → 输出:默认色的 “警告…使用默认色回显消息: 消息” ✓ 健壮性通过

📝 总结与课后作业(1 个简单任务)

通过这节课,您已经掌握了编写命令行工具的入门知识,能够使用 args.Length 和简单的 if 语句,根据用户的指令来改变程序的行为。

实践作业(1 个简单任务)

请在您的 C# 项目中完成以下任务:

询问年龄的参数程序(简单参数获取):

创建一个新程序 MyAge.exe。这个程序只接受 1 个参数(用户的年龄,比如 28)。

  • 如果用户输入了年龄 (args.Length == 1),程序就输出绿色的消息:“您今年 [年龄] 岁!”。
  • 如果用户没有输入参数 (args.Length == 0),程序就输出红色的提示:“[ 错误 ] 请在程序名后输入您的年龄!”。

请提交您的 C# 代码!