# 流程控制和错误处理
# 相关关键字
if
andelse
for
loopswhile
anddo
-while
loopsbreak
andcontinue
switch
andcase
assert
# if-else
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
Dart中if只能接收布尔表达式,不能接受其它类型值(这一点和JavaScript不同)。
# for loop
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
这里输出的是0和1,是符合预期的,避免了js中的一个陷阱:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
作为对比,由于js中var没有块级作用域,所以输出的都是2(通过把var改成let,可以解决):
var callbacks = [];
for (var i = 0; i < 2; i++) {
console.log(i)
callbacks.push(() => console.log(i));
}
callbacks.forEach((c) => c());
如果某个object
是可以迭代的,并且你使用时不关心当前迭代计数,那么使用forEach
是一个好选择:
candidates.forEach((candidate) => candidate.interview());
可迭代的类支持for-in
来迭代值,比如SET
、List
:
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
# While and do-while
while
循环,在执行前判断是否可循环:
while (!isDone()) {
doSomething();
}
do-while
循环,在循环执行后判断循环条件:
// 至少执行一次
do {
printLine();
} while (!atEndOfPage());
# Break and continue
使用break
关键字跳出循环:
// 执行break后循环终止。
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
使用continue
跳过本次循环,进入下次循环:
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
对于可迭代类,可以替换成一下的写法:
// where是对于candidates的过滤,只保留大于等于5的
// 过滤完的类,循环执行interview方法
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
// 执行结果和上面的写法一样,但是这里.where和.forEach一共执行了两次循环。比上面的写法多一次循环。
// 这里也能稍微看出命令式编程(for循环)和函数式编程的区别。
# Switch and case
Dart中的switch语句通过使用==
比较整数,字符串或编译时常量。比较的object
必须都是同一个类的实例(而不是其任何子类型),并且该类不能覆盖==
操作符。
当没有case子句匹配时,使用default子句执行代码:
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
每个case
都要有对应的break
,否则会报错,但是如果case
的内容为空,可以省略break
:
var command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
// ERROR: Missing break
case 'CLOSED':
executeClosed();
break;
}
// 空case省略break,这样当command == 'CLOSED'时,会执行'NOW_CLOSED'的代码
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // Empty case falls through.
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
如果你真的想要执行完某个case之后,再执行下面某个case,可以通过label标记来实现:
// case 'CLOSED'执行完后根据标记nowClosed,继续执行case 'NOW_CLOSED'的内容。
// 由于case 'CLOSED'执行后必然会执行case 'NOW_CLOSED',所以case 'CLOSED'没有break
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
case子句可以具有局部变量,这些变量仅在该子句的范围内可见:
switch (command) {
case 'CLOSED':
final a = 123;
print(a);
continue nowClosed;
case '123':
break;
nowClosed:
case 'NOW_CLOSED':
// print(a);
// 解开上面的注释会报错。
break;
}
# Assert
在开发期间,使用assert语句 —— assert(condition,optionalMessage);
如果布尔条件为假,则中断正常执行并抛出exception
。
示例如下:
// Make sure the variable has a non-null value.
assert(text != null);
// Make sure the value is less than 100.
assert(number < 100);
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
要将消息附加到断言,只需要添加一个字符串作为断言的第二个参数:
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
assertions何时起作用取决于您使用的工具和框架,例如flutter
会在 debug mode (opens new window)时执行assertions。
在生产代码中,assertions将被忽略,并且断言条件将不会执行。
# Exceptions
Dart可以抛出并捕获exception
。exception
是指示发生意外事件的错误。如果未捕获exception
,c程序会抛出exception
,并且通常会终止程序执行。
与Java相比,Dart的所有异常都是未经检查的exception
。方法不会声明它们可能抛出的exception
,并且您不需要捕获任何异常。
Dart提供了Exception
和Error
类型,以及许多预定义的子类型。并且你也可以定义自己的exception
。此外,Dart程序可以抛出任何非nullobject
- 不仅仅是Exception
和Error
对象 - 作为exception
。
# Throw
抛出错误:
throw FormatException('Expected at least 1 section');
抛出其他类型:
throw 'Out of llamas!';
因为抛出exception
是一个表达式,所以可以在=>
语句中以及允许表达式的任何其他地方抛出exception
:
void distanceTo(Point other) => throw UnimplementedError();
# Catch
捕获exception
会阻止exception
传播(除非你重新抛出exception
)。捕获exception
使你有机会处理它:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
要处理可能抛出多种类型exception
的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句会处理exception
。如果catch子句未指定类型,则该子句可以处理任何类型的抛出object
:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 指定的exception捕获
buyMoreLlamas();
} on Exception catch (e) {
// 任何的exception捕获
print('Unknown exception: $e');
} catch (e) {
// 捕获所有抛出的object,无论是否是exception类实例。
print('Something really unknown: $e');
}
你可以为catch()
指定一个或两个参数。第一个是抛出的exception
,第二个是堆栈跟踪(stack trace
):
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
要处理exception
,同时允许它传播,可以使用rethrow
关键字:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
# Finally
如果想要确保某些代码运行,无论是否抛出exception
,请使用finally
子句。如果没有catch子句与异常匹配,则在finally子句运行后传播exception
:
try {
breedMoreLlamas();
} finally {
// cleanLlamaStalls总会执行,即使抛出了exception
cleanLlamaStalls();
}
finally
子句在任何匹配的catch子句之后运行:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // 先处理exception
} finally {
cleanLlamaStalls(); // 然后执行cleanLlamaStalls
}
也就是说,无论你是否catch
了exception
,finally
子句都会执行。
← 运算符 认识 MongoDB →