运算符和流程控制

本节简述 Dart 的运算符、流程控制与异常处理。

运算符

以下列出了 Dart 的运算符,从高到低按照优先级排列:

描述 运算符
一元运算符(后置) ++ — ( ) [ ] . ?.
一元运算符(前置) - ! ~ ++ —
乘法运算符 * / % ~/
加法运算符 + -
移位运算符 << >>
按位与 &
按位异或 ^
按位或 |
关系与类型检测 >= > <= < as is is!
逻辑与 &&
逻辑或 ||
是否为null ??
条件运算符 ? :
级联运算符 ..
赋值运算符 = *= /= ~/= %= += -= <<= >>= &= ^= |= ??=

与其他类C语言比较,Dart的运算符大同小异,这里只选出部分Dart特有的运算符(上表中使用斜粗体的运算符)进行讲解:

运算符 介绍 示例
?. 点符号前加问号,表示当前调用只在被访问者不为null的情况下才执行 var upper = name?.toUpperCase();
is is! 检查对象是否属于或不属于某种类型 if (name is String) { … }
as 将对象转换为某类型,如果转换失败将抛出异常 name as String
?? 空值检测,如:a1 ?? a2 ,表示如果a1不为null,则直接返回a1的值,否则返回a2的值 var message = input ?? ‘Hello’;
.. 级联操作符,用于对同一对象执行一系列操作(链式操作),避免创建多余的临时变量 不使用级联:person.name = ‘bob’;person.age = 28; 使用级联: person..name = ‘bob’    ..age = 28;
??= 空值判断赋值,只在左值为null的情况下才执行赋值操作 message ??= ‘Hello’;

流程控制语句

跟操作符一样,Dart的流程控制语句也跟大部分主流语言类似,如下:

  • if 、esle
  • for 循环,包括普通的for循环和适用于可迭代对象的for…in循环(将在集合部分讲解)
  • while、do-while 循环
  • break、continue
  • switch、case
  • assert(断言)

有编程经验的开发者,对以上流程控制语句应该都不陌生,此处不再细讲。以下只对特殊的assert作简要说明:

assert语句接受一个布尔表达式,如果表达式的值为false,抛出assert异常,程序退出执行,表达式为true则正常执行。assert语句默认不开启,只在--enable-asserts选项开启时才生效,一般用于开发阶段的代码调试和验证。

以下为 assert_test.dart:

  1. main() {
  2. var name = 'jaron';
  3. assert(name == 'bob'); // 断言不成立
  4. print(name);
  5. }

开启断言运行,将抛出断言错误,程序退出执行

  1. $ dart --enable-asserts assert_test.dart // 开启 --enable-asserts

异常

Dart的异常包含两种: ExceptionErrorException表示程序逻辑执行失败,它们大都是可预料的,而且应该被捕获并进行处理。Error则表示程序发生了严重错误,通常是因为代码逻辑存在问题而导致,它们大都不可预料且应该极力避免。

如果你熟悉Java语言,请注意:Dart的异常都是非检查异常(unchecked exception),即Dart中不存在需要强制处理的异常。

异常抛出

异常抛出使用的是throw关键字,它接受任意类型的数据,或者说任意可以转换为字符串的对象都可以被作为异常抛出。在正式项目中,不建议直接抛出字符串,而应该使用Dart预定义或自定义的异常。

  1. throw 'Exception message'; // 直接抛出字符串信息
  2. throw FormatException('Exception message'); // 抛出预定义的格式化异常
  3. throw ArgumentError('Error message'); // 抛出预定义的参数错误
  4. throw CustomException('Custom message'); // 抛出自定义的异常

抛出的异常,如果没有被捕获,程序将退出执行并打印当前的堆栈信息,类似如下:

  1. Unhandled exception:
  2. Exception message
  3. #0 method (package:dart_test/exception_test.dart:2:5)
  4. #1 main (package:dart_test/exception_test.dart:6:3)
  5. #2 _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:287:32)
  6. #3 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

异常捕获

异常捕获是通过trycatchonfinally关键字实现。

异常的监控通过try,异常类型的指定通过on,异常对象和堆栈信息则由catch获得。try后可以有多条oncatch分句,且oncatch可以配合使用,没有oncatch分句可以处理任意类型的异常。无论异常是否发生,都必须执行的逻辑则可以放置于finally字句中。

需要注意的是,catch接受两个参数,第一个参数是必选的被捕获的异常对象,而第二个参数是可选的堆栈信息(StackTrace对象)。

  1. try {
  2. exceptionMethod(); // 可能抛出异常的方法
  3. } on FormatException { // 捕获FormatException异常
  4. // ......
  5. } on CustomException catch (e) { // 捕获CustomException异常
  6. print(e); // 打印错误信息
  7. // ......
  8. } catch (e, s) { // 捕获所有未被上层分句处理的异常
  9. print(e); // 打印错误和堆栈信息
  10. print(s);
  11. // ......
  12. } finally { // 总是执行,无论异常是否发生,是否被处理
  13. print('Clean up!'); // 执行清理工作
  14. }

某些场景下,可能只需要获得异常相关信息,但不需要进行处理,此时就可以使用rethrow,它的作用是将捕获的异常再次抛出:

  1. try {
  2. exceptionMethod();
  3. } catch (e) {
  4. print(e); // 打印错误信息
  5. rethrow; // 重新抛出当前捕获的异常对象 e
  6. }