第 11 节 · 第二部分 流程、方法与数据

判断

时长 6:43授课计划第 4 周

讲义

跟着视频的顺序写。每行代码都有注释,代码和运行结果都是实际运行得到的。

1判断结构

判断结构有三样东西:要测试的条件、条件为真时执行的语句(必须有)、条件为假时执行的语句(可以不写)。程序根据条件的真假,跳到不同的语句去执行,两条路最后汇到一起,接着往下走。

C# 的判断语句有:if、if...else、嵌套 if、switch、嵌套 switch,还有 Null 条件运算符 ?.。

2if 语句

if 后面的括号里放一个布尔表达式(结果是真或假),花括号里放条件为真时要执行的语句。本节例子都是顶级语句写法(不写类和 Main,代码从第一行开始执行)。

C#
1int score = 75;// score 是 75
2if (score >= 60)// 条件:score 大于等于 60?75 >= 60 为真
3{// if 块开始:条件为真才执行这里
4 Console.WriteLine("及格了");// 条件为真,输出“及格了”
5}// if 块结束
6Console.WriteLine("程序继续");// 不管真假都会执行到这一行
运行结果
及格了
程序继续

把第 1 行改成 int score = 50;,条件为假,花括号里的语句整个跳过,只剩一行:

运行结果
程序继续

括号里的条件结果必须是真或假。把比较写成一个等号,就成了赋值,编译器直接报错:

C# · 第 2 行
2if (score = 60)// 一个 = 是赋值,结果是 int 60,不是真或假
编译报错
Program.cs(2,5): error CS0029: 无法将类型“int”隐式转换为“bool”

比较是否相等要用两个等号:score == 60。

3if...else 语句

if 后面可以跟一个 else,条件为假时执行 else 里的语句。两条路只走一条,而且一定会走一条。

C#
1int score = 50;// score 是 50
2if (score >= 60)// 50 >= 60 为假
3{// if 块开始:条件为真时走这里
4 Console.WriteLine("及格了");// 这次被跳过
5}// if 块结束
6else// else:条件为假时走这里
7{// else 块开始
8 Console.WriteLine("没及格");// 条件为假,输出“没及格”
9}// else 块结束
10Console.WriteLine("程序继续");// 两条路汇到一起,输出“程序继续”
运行结果
没及格
程序继续

4else if:好几种情况

情况不止两种时,在 else 后面接着写 if,从上往下依次检查。只要有一个条件为真,执行完它的语句,后面的条件就都不再检查。

C# · 第 1-9 行
1int score = 85;// score 是 85
2if (score >= 90)// 第 1 个条件:85 >= 90 为假,往下走
3{// if 块开始
4 Console.WriteLine("优秀");// 不执行
5}// if 块结束
6else if (score >= 80)// 第 2 个条件:85 >= 80 为真
7{// 第 1 个 else if 块开始
8 Console.WriteLine("良好");// 输出“良好”,然后跳出整条链
9}// 第 1 个 else if 块结束

后面还有 else if (score >= 60) 输出“及格”和 else 输出“不及格”,85 也大于等于 60,但前面已经为真,这个条件根本不检查。

查看完整代码(17 行)
C#
1int score = 85;// score 是 85
2if (score >= 90)// 第 1 个条件:85 >= 90 为假,往下走
3{// if 块开始
4 Console.WriteLine("优秀");// 不执行
5}// if 块结束
6else if (score >= 80)// 第 2 个条件:85 >= 80 为真
7{// 第 1 个 else if 块开始
8 Console.WriteLine("良好");// 输出“良好”,然后跳出整条链
9}// 第 1 个 else if 块结束
10else if (score >= 60)// 前面已经为真,这个条件不再检查
11{// 第 2 个 else if 块开始
12 Console.WriteLine("及格");// 不执行
13}// 第 2 个 else if 块结束
14else// 上面的条件全为假时才走这里
15{// else 块开始
16 Console.WriteLine("不及格");// 不执行
17}// else 块结束
运行结果
良好

5嵌套 if

在一个 if 或 else if 里面再写一个 if。外层条件为真,才轮得到里层的判断。

C#
1int score = 95;// score 是 95
2if (score >= 60)// 外层条件:95 >= 60 为真
3{// 外层 if 块开始
4 Console.WriteLine("及格了");// 输出“及格了”
5 if (score >= 90)// 进到里面再判断:95 >= 90 为真
6 {// 里层 if 块开始
7 Console.WriteLine("还是优秀");// 输出“还是优秀”
8 }// 里层 if 块结束
9}// 外层 if 块结束
10Console.WriteLine("程序继续");// 输出“程序继续”
运行结果
及格了
还是优秀
程序继续

6switch 语句

switch 拿一个值,和好几个 case 后面的值逐个比较,看它等于哪一个,就从那个 case 进去执行。

C# · 第 1-10 行
1char grade = 'B';// grade 是字符 B(char 是单个字符,用单引号)
2switch (grade)// 拿 grade 去和下面各个 case 比
3{// switch 开始
4 case 'A':// 和 'A' 比:不相等,继续往下
5 Console.WriteLine("很棒!");// 不执行
6 break;// break:这个 case 到此结束,跳出 switch
7 case 'B':// 和 'B' 比:相等,从这里进去
8 case 'C':// case 'B' 和 case 'C' 写在一起,共用下面的语句
9 Console.WriteLine("做得好");// 输出“做得好”
10 break;// 跳出整个 switch

后面还有 case 'F' 和 default:哪个 case 都不相等时走 default,输出“无效的成绩”。跳出 switch 后,程序继续输出“你的成绩是 B”。

查看完整代码(18 行)
C#
1char grade = 'B';// grade 是字符 B(char 是单个字符,用单引号)
2switch (grade)// 拿 grade 去和下面各个 case 比
3{// switch 开始
4 case 'A':// 和 'A' 比:不相等,继续往下
5 Console.WriteLine("很棒!");// 不执行
6 break;// break:这个 case 到此结束,跳出 switch
7 case 'B':// 和 'B' 比:相等,从这里进去
8 case 'C':// case 'B' 和 case 'C' 写在一起,共用下面的语句
9 Console.WriteLine("做得好");// 输出“做得好”
10 break;// 跳出整个 switch
11 case 'F':// 已经跳出,不再比较
12 Console.WriteLine("最好再试一下");// 不执行
13 break;// case 'F' 结束
14 default:// 哪个 case 都不相等时走这里
15 Console.WriteLine("无效的成绩");// 这次不执行
16 break;// default 结束
17}// switch 结束
18Console.WriteLine("你的成绩是 " + grade);// 跳出后程序继续,输出“你的成绩是 B”
运行结果
做得好
你的成绩是 B

C# 规定每个 case 的语句最后必须跳出去。漏写 break,编译器报错;C 语言会接着执行下一个 case(叫“贯穿”),C# 不允许。

C# · 第 4-6 行
4case 'A':// case 'A' 开始
5 Console.WriteLine("很棒!");// 后面漏写了 break
6case 'B':// 直接接下一个 case
编译报错
Program.cs(4,5): error CS0163: 控制不能从一个 case 标签(“case 'A':”)贯穿到另一个 case 标签

7嵌套 switch

switch 里面也能再放一个 switch。里层的 break 只跳出里层,外层要靠自己的 break 跳出。例子里 a 是 100、b 是 200,最后两行打印 a 和 b 的值。

C# · 第 5-13 行
5case 100:// a 等于 100,从这里进去
6 Console.WriteLine("这是外部 switch 的一部分");// 外层输出
7 switch (b)// 里层 switch:b 是 200
8 {// 里层 switch 开始
9 case 200:// b 等于 200,从这里进去
10 Console.WriteLine("这是内部 switch 的一部分");// 里层输出
11 break;// 只跳出里层 switch
12 }// 里层 switch 结束
13 break;// 外层的 break,跳出外层 switch
运行结果
这是外部 switch 的一部分
这是内部 switch 的一部分
a 的值是 100
b 的值是 200

8Null 条件运算符 ?.

?. 是 C# 6.0 引入的,它是运算符,不是语句,但它也在做判断。name 是空值(null,什么都没有)时直接取它的 Length(字符串的长度),程序一运行就抛出空引用异常(异常是程序运行时出的错,后面会讲):

C#
1string? name = null;// string? 表示 name 可以是空值,这里就是空值
2Console.WriteLine(name.Length);// 对空值取 Length:运行时抛出异常

运行结果第一行是 Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.(未处理的空引用异常)。以前要先用 if 判断:

C# · 第 2-5 行
2if (name != null)// 先判断 name 不是空值
3{// if 块开始
4 Console.WriteLine(name.Length);// 不是空值才去取 Length
5}// if 块结束

换成 ?.,判断就藏在运算符里:不是空值照常取 Length;是空值,整个式子的结果就是空值,不报错。

C#
1string? name = null;// name 是空值
2Console.WriteLine("长度:" + name?.Length);// name?.Length 得到空值,“长度:”后面什么也没有
3name = "小明";// name 赋值为“小明”
4Console.WriteLine("长度:" + name?.Length);// 不是空值,照常取 Length:2
运行结果
长度:
长度:2

9条件运算符 ?:

一般形式是 Exp1 ? Exp2 : Exp3,冒号夹在两个结果中间。先算 Exp1:为真,整个式子的值是 Exp2;为假,是 Exp3。

C#
1int score = 75;// score 是 75
2string result = score >= 60 ? "及格" : "不及格";// 75 >= 60 为真,选出“及格”,赋给 result
3Console.WriteLine(result);// 输出“及格”
运行结果
及格

if else 是从两段语句里选一段来执行;?: 是从两个值里选一个,算出来的值要用掉。单独写成一句会报错:

C# · 第 2 行
2score >= 60 ? "及格" : "不及格";// 选出的值没人用,不能单独当一句
编译报错
Program.cs(2,1): error CS0201: 只有 assignment、call、increment、decrement 和 new 对象表达式可用作语句

两边是 Console.WriteLine 也不行,它没有值可选:

C# · 第 2 行
2string result = score >= 60 ? Console.WriteLine("及格") : Console.WriteLine("不及格");// WriteLine 不返回值(void)
编译报错
Program.cs(2,17): error CS0029: 无法将类型“void”隐式转换为“string”

所以只有两边都能算出一个值时,?: 才能替代 if else。

10小结

  • if 管一种情况,if else 管两种,else if 管好几种;嵌套就是判断里面套判断。
  • 拿一个值和几个固定值比,用 switch,每个 case 最后别忘了 break。
  • 可能是空值,用 ?.;只是二选一个值,用 ?:。

下一节:同一件事要做一百遍,怎么办?

检查题

5 道,选完马上告诉你对不对。

  1. 1int score = 85; 后面是 if (score >= 90) … else if (score >= 80) 输出“良好” … else if (score >= 60) 输出“及格”,输出是?

  2. 2if (score = 60) 会怎样?

  3. 3一个 case 的语句后面没写 break,直接接下一个 case,C# 会?

  4. 4string? name = null; Console.WriteLine("长度:" + name?.Length); 输出?

  5. 5下面哪句能编译?