1判断结构
判断结构有三样东西:要测试的条件、条件为真时执行的语句(必须有)、条件为假时执行的语句(可以不写)。程序根据条件的真假,跳到不同的语句去执行,两条路最后汇到一起,接着往下走。
C# 的判断语句有:if、if...else、嵌套 if、switch、嵌套 switch,还有 Null 条件运算符 ?.。
2if 语句
if 后面的括号里放一个布尔表达式(结果是真或假),花括号里放条件为真时要执行的语句。本节例子都是顶级语句写法(不写类和 Main,代码从第一行开始执行)。
| 1 | int score = 75; | // score 是 75 |
| 2 | if (score >= 60) | // 条件:score 大于等于 60?75 >= 60 为真 |
| 3 | { | // if 块开始:条件为真才执行这里 |
| 4 | Console.WriteLine("及格了"); | // 条件为真,输出“及格了” |
| 5 | } | // if 块结束 |
| 6 | Console.WriteLine("程序继续"); | // 不管真假都会执行到这一行 |
及格了 程序继续
把第 1 行改成 int score = 50;,条件为假,花括号里的语句整个跳过,只剩一行:
程序继续
括号里的条件结果必须是真或假。把比较写成一个等号,就成了赋值,编译器直接报错:
| 2 | if (score = 60) | // 一个 = 是赋值,结果是 int 60,不是真或假 |
Program.cs(2,5): error CS0029: 无法将类型“int”隐式转换为“bool”
比较是否相等要用两个等号:score == 60。
3if...else 语句
if 后面可以跟一个 else,条件为假时执行 else 里的语句。两条路只走一条,而且一定会走一条。
| 1 | int score = 50; | // score 是 50 |
| 2 | if (score >= 60) | // 50 >= 60 为假 |
| 3 | { | // if 块开始:条件为真时走这里 |
| 4 | Console.WriteLine("及格了"); | // 这次被跳过 |
| 5 | } | // if 块结束 |
| 6 | else | // else:条件为假时走这里 |
| 7 | { | // else 块开始 |
| 8 | Console.WriteLine("没及格"); | // 条件为假,输出“没及格” |
| 9 | } | // else 块结束 |
| 10 | Console.WriteLine("程序继续"); | // 两条路汇到一起,输出“程序继续” |
没及格 程序继续
4else if:好几种情况
情况不止两种时,在 else 后面接着写 if,从上往下依次检查。只要有一个条件为真,执行完它的语句,后面的条件就都不再检查。
| 1 | int score = 85; | // score 是 85 |
| 2 | if (score >= 90) | // 第 1 个条件:85 >= 90 为假,往下走 |
| 3 | { | // if 块开始 |
| 4 | Console.WriteLine("优秀"); | // 不执行 |
| 5 | } | // if 块结束 |
| 6 | else if (score >= 80) | // 第 2 个条件:85 >= 80 为真 |
| 7 | { | // 第 1 个 else if 块开始 |
| 8 | Console.WriteLine("良好"); | // 输出“良好”,然后跳出整条链 |
| 9 | } | // 第 1 个 else if 块结束 |
后面还有 else if ( 输出“及格”和 else 输出“不及格”,85 也大于等于 60,但前面已经为真,这个条件根本不检查。
查看完整代码(17 行)
| 1 | int score = 85; | // score 是 85 |
| 2 | if (score >= 90) | // 第 1 个条件:85 >= 90 为假,往下走 |
| 3 | { | // if 块开始 |
| 4 | Console.WriteLine("优秀"); | // 不执行 |
| 5 | } | // if 块结束 |
| 6 | else if (score >= 80) | // 第 2 个条件:85 >= 80 为真 |
| 7 | { | // 第 1 个 else if 块开始 |
| 8 | Console.WriteLine("良好"); | // 输出“良好”,然后跳出整条链 |
| 9 | } | // 第 1 个 else if 块结束 |
| 10 | else if (score >= 60) | // 前面已经为真,这个条件不再检查 |
| 11 | { | // 第 2 个 else if 块开始 |
| 12 | Console.WriteLine("及格"); | // 不执行 |
| 13 | } | // 第 2 个 else if 块结束 |
| 14 | else | // 上面的条件全为假时才走这里 |
| 15 | { | // else 块开始 |
| 16 | Console.WriteLine("不及格"); | // 不执行 |
| 17 | } | // else 块结束 |
良好
5嵌套 if
在一个 if 或 else if 里面再写一个 if。外层条件为真,才轮得到里层的判断。
| 1 | int score = 95; | // score 是 95 |
| 2 | if (score >= 60) | // 外层条件:95 >= 60 为真 |
| 3 | { | // 外层 if 块开始 |
| 4 | Console.WriteLine("及格了"); | // 输出“及格了” |
| 5 | if (score >= 90) | // 进到里面再判断:95 >= 90 为真 |
| 6 | { | // 里层 if 块开始 |
| 7 | Console.WriteLine("还是优秀"); | // 输出“还是优秀” |
| 8 | } | // 里层 if 块结束 |
| 9 | } | // 外层 if 块结束 |
| 10 | Console.WriteLine("程序继续"); | // 输出“程序继续” |
及格了 还是优秀 程序继续
6switch 语句
switch 拿一个值,和好几个 case 后面的值逐个比较,看它等于哪一个,就从那个 case 进去执行。
| 1 | char grade = 'B'; | // grade 是字符 B(char 是单个字符,用单引号) |
| 2 | switch (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 行)
| 1 | char grade = 'B'; | // grade 是字符 B(char 是单个字符,用单引号) |
| 2 | switch (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 结束 |
| 18 | Console.WriteLine("你的成绩是 " + grade); | // 跳出后程序继续,输出“你的成绩是 B” |
做得好 你的成绩是 B
C# 规定每个 case 的语句最后必须跳出去。漏写 break,编译器报错;C 语言会接着执行下一个 case(叫“贯穿”),C# 不允许。
| 4 | case 'A': | // case 'A' 开始 |
| 5 | Console.WriteLine("很棒!"); | // 后面漏写了 break |
| 6 | case 'B': | // 直接接下一个 case |
Program.cs(4,5): error CS0163: 控制不能从一个 case 标签(“case 'A':”)贯穿到另一个 case 标签
7嵌套 switch
switch 里面也能再放一个 switch。里层的 break 只跳出里层,外层要靠自己的 break 跳出。例子里 a 是 100、b 是 200,最后两行打印 a 和 b 的值。
| 5 | case 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(字符串的长度),程序一运行就抛出空引用异常(异常是程序运行时出的错,后面会讲):
| 1 | string? name = null; | // string? 表示 name 可以是空值,这里就是空值 |
| 2 | Console.WriteLine(name.Length); | // 对空值取 Length:运行时抛出异常 |
运行结果第一行是 Unhandled exception. System.(未处理的空引用异常)。以前要先用 if 判断:
| 2 | if (name != null) | // 先判断 name 不是空值 |
| 3 | { | // if 块开始 |
| 4 | Console.WriteLine(name.Length); | // 不是空值才去取 Length |
| 5 | } | // if 块结束 |
换成 ?.,判断就藏在运算符里:不是空值照常取 Length;是空值,整个式子的结果就是空值,不报错。
| 1 | string? name = null; | // name 是空值 |
| 2 | Console.WriteLine("长度:" + name?.Length); | // name?.Length 得到空值,“长度:”后面什么也没有 |
| 3 | name = "小明"; | // name 赋值为“小明” |
| 4 | Console.WriteLine("长度:" + name?.Length); | // 不是空值,照常取 Length:2 |
长度: 长度:2
9条件运算符 ?:
一般形式是 Exp1 ? Exp2 : Exp3,冒号夹在两个结果中间。先算 Exp1:为真,整个式子的值是 Exp2;为假,是 Exp3。
| 1 | int score = 75; | // score 是 75 |
| 2 | string result = score >= 60 ? "及格" : "不及格"; | // 75 >= 60 为真,选出“及格”,赋给 result |
| 3 | Console.WriteLine(result); | // 输出“及格” |
及格
if else 是从两段语句里选一段来执行;?: 是从两个值里选一个,算出来的值要用掉。单独写成一句会报错:
| 2 | score >= 60 ? "及格" : "不及格"; | // 选出的值没人用,不能单独当一句 |
Program.cs(2,1): error CS0201: 只有 assignment、call、increment、decrement 和 new 对象表达式可用作语句
两边是 Console. 也不行,它没有值可选:
| 2 | string 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。
- 可能是空值,用
?.;只是二选一个值,用?:。
下一节:同一件事要做一百遍,怎么办?
