愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Dart学习笔记(四):流程控制与异常处理

一、流程控制

Dart里的流程控制与大多数编程语言类似,即为:

  • 条件控制:ifelse
  • 循环:forwhiledo-while
  • 跳出循环、下个循环:breakcontinue
  • 分支选择:switchcase
  • 断言:assert

以下为一些注意事项:

1、明确的bool类型

在条件语句里,不会隐式地进行类型转化,必须明确地使表达式的计算结果为bool类型

2、for循环中的闭包捕获

for循环中,出现的闭包,Dart会捕获其值,这点和JavaScript中使用var不一样:

var callbacks = [];
for (var i = 0; i < 2; i++) {
    callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
// 输出:0 1

如果要遍历的对象实现了iterable接口,那么可以使用forEach方法(不过forEach方法不能得到索引,这点也是和JavaScript不一样的):

candidates.forEach((candidate) => candidate.interview());

ListSet这些实现了iterable接口的类,他们还支持for-in形式的遍历,如下:

var collection = [0, 1, 2];
for (var x in collection) {
    print(x);
}

3、switch/case

Dart里,switch适用于比较integerstring和编译时常量(const),它使用的是==进行比较。此外,有个强制要求,那就是:

每个 非空 分支下,必须包含break语句
如:

var command = 'OPEN';
switch (command) {
    case 'CLOSE':
        executeClosed();
        break; // 必须,如果没有提供,则会报错
    case 'OPEN':
        executeOpen();
        break;
    default:
        executeUnknown();
}

如果分支是空的,那么可以省略break,如下:

var age = 1;
switch (age) {
    case 1: // 可以省略break
    case 2: // 可以省略break
    case 3:
        print('Hello, little baby');
        break;
}

每个case里的变量是局部变量,如下:

var n = 1;
// 以下由于desc是属于每个case的局部变量,所以不会报错
switch (n) {
    case 1:
        var desc = '1st';
        print(desc);
        break;
    case 2:
        var desc = '2nd';
        print(desc);
        break;
    case 3:
        var desc = '3rd';
        print(desc);
        break;
    default:
        var desc = '${n}th';
        print(desc);
}

我们还可以对某个节点加上标签,然后跳转到对应的标签,如:

var age = 18;
switch (age) {
    case 18:
        print('You are an adult.');
        continue showAge;
    showAge:
    default:
        print('You\'re $age');
}
/*
输出:
You are an adult.
You're 18
*/

4、断言(assert)

断言的作用是:如果表达式的求值结果不满足需要,则打断代码的执行,如下:

assert(text !== null);
// 那么,当text是null值的时候,代码的执行会被打断
// 如果text是非null值的时候,则代码会继续执行

传入assert的参数,可以是任意表达式或者方法,只要返回值是bool就可以,当断言失败时(返回false),会抛出AssertionError异常

注意:断言只有在检查模式下运行有效,在生产模式下是不会运行的


二、异常处理

Dart里,异常表示一些未知的错误情况,如果异常没有被捕获,则异常会抛出,导致抛出异常的代码终止执行。在Dart里有两个类型表示异常,即为:ExeceptionError,其他的子类型和自定义异常类型均继承自这两个基础异常类型。此外,还可以抛出任意的非null的对象作为异常,而不一定是Exception或者Error及其子类型。

1、throw

throw用于主动抛出异常,如下:

throw new FormatException('Expected at least 1 section'); // 抛出 Exception 子类型
throw 'Out ot memory'; // 抛出非`null`对象
distanceTo(Point other) => throw new UnimplementedError(); // throw是表达式,可以在可以使用表达式的地方使用

2、catch

捕获异常可以避免异常继续传递(除非重新throw异常),因此捕获异常可以获得处理异常的机会。和Javascript不同的是,由于Dart有类型系统,所以可以针对特定类型的异常做处理,如下:

try {
    aFunctionThatMayCauseException();
} on SomeException {
    // 处理异常
}

所以也可以把catch当做是默认情况下的异常处理分支,如:

try {
    aFunctionThatMayCauseException();
} on AException {
    // 处理异常
} on BException catch (e) {
    // 捕获异常对象e
} catch (e) {
    // 默认情况下的异常处理
}

此外,Dart中的catch提供了两个参数,即为:catch(errorObject, errorStack),其中第一个参数提供了抛出的异常对象,而第二个参数则是StackTrace类的实例,提供了异常的堆栈信息,如:

try {
    aFunctionThatMayCauseException();
} catch (e, s) {
    // ...
}

可以使用rethrow关键字,重新抛出异常,如:

final foo = '';
void misbehave() {
    try {
        foo = 'You can\'t change a final variable\'s value.';
    } catch (e) {
        print('misbehave() partially handled ${e.runtimeType}.');
        rethrow;
    }
}
void main() {
    try {
        misbehave();
    } catch (e) {
        print('main() finished handling ${e.runtimeType}.');
    }
}

3、finally

finally语句的作用,则在于无论是否捕获异常,都必须要执行,如下:

try {
    aFunctionThatMayCauseException();
} finally {
    // 无论有没有异常抛出,这里的语句都会执行
}