if 与 switch 语句
程序的控制结构包括:
- 顺序(顺序执行)
- 分支(
if
、switch
) - 循环(
for
、while
)
分支语句包括 if
和 switch
语句。
if
下例中,如果 isSunny
的值为true
,就执行打印,否则什么也不做。
// ch0201_1.dart
import 'dart:math';
void main() {
final rand = Random();
final isSunny = rand.nextInt(10) % 2 == 0;
if (isSunny) {
print("Let's go to the park");
}
}
flowchart TD B{条件}-->|true| C(代码块) C --> E([End]) B -->|false| E
if
有一个可选的else
分支。
if (isSunny) {
print("Let's go to the park");
} else {
print("It's not sunny");
}
flowchart TD B{条件}-->|true| C(主分支) C --> E([End]) B -->|false| D(else分支) D --> E
还可以使用 else if
,像下面这样。
if (isSunny) {
print("Let's go to the park");
} else if (isRaining) {
print("Let's bring our raincoats");
} else {
print("Let's stay home");
}
从语法上讲,上例中最后的else
分支也是可选的。
if-case
从 Dart 3.0 开始, if
语句支持 case
子句,case
后面接一个模式(pattern,第3章内容)。
// ch0201_2.dart
void main() {
var dat = [1, 2];
if (dat case [_, _]) { // 1
print('matched'); // 2
} else {
print('not matched'); // 2a
}
if (dat case [int x, int y]) { // 3
print('x=$x y=$y'); // 4
}
}
- 使用 if-case 检查
dat
是否匹配一个包含2个元素的列表; - 如果匹配,就打印字符串
"matched"
,否则打印"not matched"
(2a); - 使用 if-case 将
dat
与列表进行匹配,并捕获列表中的元素。
switch
如果需要匹配多个case,应使用switch
语句,而非一个又一个的else if
。
flowchart TD A{switch}-->|case1| B(case1) A --> |case2| C(case2) A --> |case...| D(case...) A --> |default| E(default分支) B --> Z([End]) C --> Z D --> Z E --> Z
// ch0201_3.dart
void main() {
var status = 'done';
switch (status) {
case 'done':
print('done');
case 'ongoing':
print('ongoing');
case 'pending':
print('pending');
default: // case _:
print('unknown status: $status');
}
}
如果其他case都不匹配,则执行default case的代码,也就是 default
或 case _
标记的代码 。default case 要放在最后。
fallthrough 与 标签(lablel)
空case(empty case)是指不做任何处理的case。默认情况下空case会fall through到下一个case,使得cases可以共享代码块。如果想让空case fall through到其他的case(非下一个case),使用标签(label)和 continue
来实现。
// ch0201_4.dart
void main() {
var status = 'canceled';
switch (status) {
case 'canceled': // 1 empty case
case 'done': // 1a
print('done');
todo: // 2
case 'todo':
print('todo');
case 'pending':
print('pending');
continue todo; // 3
case 'ongoing':
print('ongoing');
case _:
print('unknown status: $status');
}
}
- 这是一个空case(
canceled
),它没有对应的代码块,fall through 到下一个case(done
), 这两个case共享了代码块(print('done')
) ; 此外这两个case也可以写成
case 'canceled' || 'done':
print('done');
- 这里定义了一个标签
todo
; - 在执行完上一行代码后(
print('pending')
),跳转至todo
标签所在位置继续执行(print('todo')
)。
如果不想让空case fall through, 使用 break
即可。
case 'canceled':
break;
switch
表达式
switch
表达式将返回一个值,这个值来自于匹配的case的表达式。先来看一个普通的switch语句:
// ch0201_5.dart
void main() {
var x = 1.0, y = 2.0;
var op = '+';
double r;
switch (op) {
case '+':
r = x + y;
case '-':
r = x - y;
case '*':
r = x * y;
case '/':
r = x / y;
case '%':
r = x % y;
case _:
throw 'unknown op: $op';
}
print('$x$op$y=$r');
}
用switch
表达式改写:
// ch0201_6.dart
void main() {
var x = 1.0, y = 2.0;
var op = '+';
var r = switch (op) {
'+' => x + y,
'-' => x - y,
'*' => x * y,
'/' => x / y,
'%' => x % y,
_ => throw 'unknown op: $op',
};
print('$x$op$y=$r');
}
在语法方面,与switch
语句相比,switch
表达式:
- case不以
case
关键字开头; - case主体是一个单一的表达式,而不是一系列的语句;
- 每个case都必须有一个主体;对于空case,没有隐式的 fallthrough;
- case模式与其主体使用
=>
而不是:
分隔; - 各个case之间用
,
分隔(允许在结尾添加可选的,
); - default case 只能用
_
, 无法用default
。
详尽性检查
下面这段代码将引起编译错误:
//ch0201_7.dart
void main() {
bool? win;
var score = switch (win) {
true => 2,
false => 0,
};
// Error: The type 'bool?' is not exhaustively matched by the switch cases since it doesn't match 'null'.
// Try adding a wildcard pattern or cases that match 'null'.
// var score = switch (win) {
// ^
}
对于一个 boo?
类型的变量,它的值是穷举的,即 true
、false
和 null
,Dart编译器将对这类可穷举类型的变量进行详尽性检查。上例的编译错误告诉我们缺少了对 null
case 的处理。常见的可穷举类型还有bool
、枚举(Enum)和密封类(sealed class)。
下例演示了针对枚举的详尽性检查。
// ch0201_8.dart
void main() {
var x = 1.0, y = 2.0;
var op = Op.plus;
var r = switch (op) {
Op.plus => x + y,
// Op.minus => x - y,
};
// Error: The type 'Op' is not exhaustively matched by the switch cases since it doesn't match 'Op.minus'.
// - 'Op' is from 'bin/ch02/ch0201_8.dart'.
// Try adding a wildcard pattern or cases that match 'Op.minus'.
// var r = switch (op) {
// ^
}
enum Op { plus, minus }
下例演示了针对密封类的详尽性检查。一个密封类的子类是有限个的,switch
语句或表达式将对其子类进行详尽性检查。
// ch0201_9.dart
void main() {
Op op = Plus(1.0, 2.0);
var r = switch (op) {
Plus(:double x, :double y) => x + y,
// Minus(:double x, :double y) => x - y,
};
// Error: The type 'Op' is not exhaustively matched by the switch cases since it doesn't match 'Minus()'.
// - 'Op' is from 'bin/ch02/ch0201_9.dart'.
// Try adding a wildcard pattern or cases that match 'Minus()'.
// var r = switch (op) {
// ^
}
sealed class Op {
final double x;
final double y;
Op(this.x, this.y);
}
class Plus extends Op {
Plus(super.x, super.y);
}
class Minus extends Op {
Minus(super.x, super.y);
}
第4-5章将详细讨论类的相关概念。
守护子句
守护子句(guard clause)是可选的,它出现在case子句之后,使用关键字 when
。守护子句可以出现在 if-case 以及 switch
语句和表达式中。
// ch0201_a.dart
void main() {
var year = 2025;
bool showAges = true;
switch (year) {
case >= 2000 when showAges && year < 2010:
print('2000s');
case >= 2010 when showAges && year < 2020:
print('2010s');
case >= 2020 when showAges && year < 2030:
print('2020s');
}
var dat = [3, 4];
if (dat case [int x, int y] when x < y) {
print('x=$x y=$y');
}
}