文章

Dart控制流

流程控制语句 if and else , for, while and do-while , break and continue ,switch语句和表达式

Dart语言详解

循环

For loops

您可以使用标准for循环进行迭代。例如:

1
2
3
4
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('!');
}

Dartfor循环内的闭包会捕获索引的 。这避免了 JavaScript 中常见的陷阱。例如,考虑:

1
2
3
4
5
6
7
8
var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}

for (final c in callbacks) {
  c();
}

正如预期的那样,输出是 0,然后是 1。相反,该示例将在 JavaScript 中打印 2,然后打印 2。

有时,在迭代Iterable类型(例如 List 或 Set)时,可能不需要知道当前迭代计数器。在这种情况下,使用 for-in 循环来获得更简洁的代码:

1
2
3
for (final candidate in candidates) {
  candidate.interview();
}

要处理从 iterable 获取的值,还可以在 for-in 循环中使用模式 pattern

1
2
3
for (final Candidate(:name, :yearsExperience) in candidates) {
  print('$name has $yearsExperience of experience.');
}

可迭代类还有一个 forEach() 方法作为另一个选项:

1
2
var collection = [1, 2, 3];
collection.forEach(print); // 1 2 3

While and do-while

while 循环判断循环之前的条件:

1
2
3
while (!isDone()) {
  doSomething();
}

do-while循环判断循环后的条件

1
2
3
do {
  printLine();
} while (!atEndOfPage());

Break and continue

使用break来停止循环:

1
2
3
4
while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

使用 continue 跳到下一个循环迭代:

1
2
3
4
5
6
7
for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}

如果您使用的是 Iterable(例如列表或集合),则编写前面示例的方式可能会有所不同:

1
2
3
candidates
    .where((c) => c.yearsExperience >= 5)
    .forEach((c) => c.interview());

分支

If

Dart 支持if带有可选else子句的语句。后面括号中的条件必须是计算结果为布尔值的表达式

1
2
3
4
5
6
7
if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

If-case

Dart if 语句支持 case 子句后跟一个pattern

1
if (pair case [int x, int y]) return Point(x, y);

如果模式与值匹配,则分支将使用模式在范围内定义的任何变量执行。

在前面的例子中,列表模式[int x, int y]与值pair匹配,因此分支返回 Point(x, y)使用pattern 定义的变量xy

否则,控制流将前进到else要执行的分支(如果有):

1
2
3
4
5
if (pair case [int x, int y]) {
  print('Was coordinate array $x,$y');
} else {
  throw FormatException('Invalid coordinates.');
}

if-case 语句提供了一种针对 单一 模式进行匹配和解构的方法。要针对 多种 模式测试某个值,请使用switch

Switch

语句switch根据一系列case选择值表达式。每个case语句都是要匹配值的模式。您可以对case使用任何类型的模式。

当值与 case 的模式匹配时,case 主体将执行。非空case子句在完成后跳转到 switch 的末尾。它们不需要break语句。结束非空case子句的其他有效方式是 continuethrowreturn语句。

当没有子句匹配时,使用default或通配符_子句来执行代码case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
  case 'PENDING':
    executePending();
  case 'APPROVED':
    executeApproved();
  case 'DENIED':
    executeDenied();
  case 'OPEN':
    executeOpen();
  default:
    executeUnknown();
}

空case会直接跳转到下一个case,从而允许case共享一个主体。对于不会直接跳转的空case,使用break其主体。对于非连续直接跳转,可以使用continue语句和标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (command) {
  case 'OPEN':
    executeOpen();
    continue newCase; // Continues executing at the newCase label.

  case 'DENIED': // Empty case falls through.
  case 'CLOSED':
    executeClosed(); // Runs for both DENIED and CLOSED,

  newCase:
  case 'PENDING':
    executeNowClosed(); // Runs for both OPEN and PENDING.
}

Switch 表达式

switch 表达式根据匹配的 case 主体生成值。 可以在 Dart 允许表达式的任何地方使用 switch 表达式,但表达式语句的开头除外。例如:

1
2
3
4
5
var x = switch (y) { ... };

print(switch (x) { ... });

return switch (x) { ... };

如果要在表达式语句的开头使用 switch,使用switch 语句。

Switch 表达式允许你重写 switch语句,如下所示:

1
2
3
4
5
6
7
8
9
10
11
// Where slash, star, comma, semicolon, etc., are constant variables...
switch (charCode) {
  case slash || star || plus || minus: // Logical-or pattern
    token = operator(charCode);
  case comma || semicolon: // Logical-or pattern
    token = punctuation(charCode);
  case >= digit0 && <= digit9: // Relational and logical-and patterns
    token = number();
  default:
    throw FormatException('Invalid');
}

变成这样的 表达式:

1
2
3
4
5
6
token = switch (charCode) {
  slash || star || plus || minus => operator(charCode),
  comma || semicolon => punctuation(charCode),
  >= digit0 && <= digit9 => number(),
  _ => throw FormatException('Invalid')
};

switch表达式的语法与switch语句的语法不同:

  • Case不以 case 关键字开头。
  • Case主体是单个表达式而不是一系列语句。
  • 每个Case必须有一个主体;对于空的情况没有隐式的失败。
  • 使用 => 而不是 : 将Case模式与其主体分开。
  • Case由 , 分隔(并且允许使用可选的尾随 , )
  • 默认情况下只能使用_,而不是同时允许default_

详尽性检查 Exhaustiveness checking

详尽性检查是一项功能,如果某个值有可能进入 switch 但不符合任何情况,则会报告编译时错误。

1
2
3
4
5
6
7
// Non-exhaustive switch on bool?, missing case to match null possibility:
switch (nullableBool) {
  case true:
    print('yes');
  case false:
    print('no');
}

默认情况(default_)涵盖了可以流经 switch 的所有可能值。这使得任何类型的 switch 都具有详尽性。

枚举和sealed类型对于切换尤其有用,因为即使没有默认值,它们的可能值也是已知的并且完全可枚举。在切换该类的子类型时,使用类上的sealed修饰符可以启用详尽性检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

double calculateArea(Shape shape) => switch (shape) {
      Square(length: var l) => l * l,
      Circle(radius: var r) => math.pi * r * r
    };

如果有人要添加 的新子类Shape,则此switch表达式将不完整。 详尽性检查会告知您缺少的子类型。 这允许您以某种 函数式代数数据类型样式使用 Dart 。

保护条款 Guard clause

要在子句后设置可选的保护子句case,请使用关键字when。保护子句可以跟在if case、 以及switch语句和表达式之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Switch statement:
switch (something) {
  case somePattern when some || boolean || expression:
    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
    body;
}

// Switch expression:
var value = switch (something) {
  somePattern when some || boolean || expression => body,
  //               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
}

// If-case statement:
if (something case somePattern when some || boolean || expression) {
  //                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
  body;
}

匹配后,保护条款会评估任意布尔表达式。这允许您对是否应执行案例主体添加进一步的约束。当保护子句评估为 false 时,执行将继续执行下一个案例,而不是退出整个 switch。

本文由作者按照 CC BY 4.0 进行授权