Loops 循环
for
loopsfor
循环while
anddo while
loops
while
和do while
循环break
andcontinue
中断
并继续
你还可以使用以下方法在 Dart 中作控制流:
- Branching, like
if
andswitch
分支 ,如if
和switch
- Exceptions, like
try
,catch
, andthrow
异常 ,如try
、catch
和throw
For loops For 循环
您可以使用标准的 for
循环进行迭代。例如:
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
Dart 的 for
循环中的闭包捕获了索引的值 。这避免了 JavaScript 中常见的陷阱。例如,请考虑:
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
循环来获得更简洁的代码:
for (var candidate in candidates) {
candidate.interview();
}
在前面的示例循环中,candidate
在循环体中定义,并设置为一次引用 candidate
中的一个值。 candidate
是局部变量 。在循环体中重新分配 candidate
只会更改该迭代的局部变量,而不会修改可迭代的原始 candidates
。
要处理从 iterable 获得的值,您还可以在 for-in
循环中使用模式 :
for (final Candidate(:name, :yearsExperience) in candidates) {
print('$name has $yearsExperience of experience.');
}
Iterable 类也有一个 forEach() 方法作为另一个选项:
var collection = [1, 2, 3];
collection.forEach(print); // 1 2 3
While and do-while While 和 do-while
while
循环在循环之前评估条件:
while (!isDone()) {
doSomething();
}
do-while
循环在循环后评估条件:
do {
printLine();
} while (!atEndOfPage());
Break and continue 中断并继续
使用 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();
}
如果你使用的是 Iterable,比如 list 或 set,你编写前面的例子的方式可能会有所不同:
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
Labels 标签
标签是后跟冒号 (labelName:
) 的标识符 ,您可以将其放在语句之前以创建 labeled 语句 。Loops 和 switch case 通常用作 labeled 语句。稍后可以在 break
或 continue
语句中引用 labeled 语句,如下所示:
-
break labelName;
终止 labeled 语句的执行。 这对于在以下情况下跳出特定的外部循环非常有用 在嵌套循环中。 -
continue labelName;
跳过 标记的 statement 循环并继续下一次迭代。 -
标签用于管理控制流。它们通常与 loops 和 switch case 一起使用,并允许您指定要跳出或继续的语句,而不是默认影响最里面的循环。
Labels in for loop using break
使用 break
的 for 循环中的标签
以下代码演示了名为 outerLoop
的标签的用法 在带有 break
语句的 for
循环中:
outerLoop:
for (var i = 1; i <= 3; i++) {
for (var j = 1; j <= 3; j++) {
print('i = $i, j = $j');
if (i == 2 && j == 2) {
break outerLoop;
}
}
}
print('outerLoop exited');
在前面的示例中,当 i == 2
且 j == 2
时,break outerLoop;
语句会停止内部和外部循环。因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 2, j = 2
outerLoop exited
Labels in for loop using continue
使用 continue
的 for 循环中的标签
以下代码演示了名为 outerLoop
的标签的用法 在带有 continue
语句的 for
循环中:
outerLoop:
for (var i = 1; i <= 3; i++) {
for (var j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
continue outerLoop;
}
print('i = $i, j = $j');
}
}
在前面的示例中,当 i == 2
且 j == 2
时,continue outerLoop;
跳过 i = 2
的其余迭代,并移动到 i = 3
。因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 3, j = 1
i = 3, j = 2
i = 3, j = 3
Labels in while loop using break
使用 break
的 while 循环中的标签
以下代码演示了如何使用 break
语句在 while
循环中使用名为 outerLoop
的标签:
var i = 1;
outerLoop:
while (i <= 3) {
var j = 1;
while (j <= 3) {
print('i = $i, j = $j');
if (i == 2 && j == 2) {
break outerLoop;
}
j++;
}
i++;
}
print('outerLoop exited');
在前面的示例中,当 i == 2
和 j == 2
时,程序会同时跳出内部和外部 while
循环。因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 2, j = 2
outerLoop exited
Labels in while loop using continue
使用 continue
的 while 循环中的标签
以下代码演示了如何使用 continue
语句在 while
循环中使用名为 outerLoop
的标签:
var i = 1;
outerLoop:
while (i <= 3) {
var j = 1;
while (j <= 3) {
if (i == 2 && j == 2) {
i++;
continue outerLoop;
}
print('i = $i, j = $j');
j++;
}
i++;
}
在前面的示例中,跳过了 i = 2
和 j = 2
的迭代,循环直接移动到 i = 3
。因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 3, j = 1
i = 3, j = 2
i = 3, j = 3
Labels in do-while loop using break
使用 break
的 do-while 循环中的标签
以下代码演示了如何使用 break 语句在
do while
循环中使用名为 outerLoop
的标签:
var i = 1;
outerLoop:
do {
var j = 1;
do {
print('i = $i, j = $j');
if (i == 2 && j == 2) {
break outerLoop;
}
j++;
} while (j <= 3);
i++;
} while (i <= 3);
print('outerLoop exited');
在前面的示例中,当 i == 2
且 j == 2
.因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 2, j = 2
outerLoop exited
Labels in do-while loop using continue
使用 continue
的 do-while 循环中的标签
以下代码演示了如何使用 continue 语句在
do while
循环中使用名为 outerLoop
的标签:
var i = 1;
outerLoop:
do {
var j = 1;
do {
if (i == 2 && j == 2) {
i++;
continue outerLoop;
}
print('i = $i, j = $j');
j++;
} while (j <= 3);
i++;
} while (i <= 3);
在前面的示例中,循环跳过 i = 2
和 j = 2
,直接移动到 i = 3
。因此,输出为:
i = 1, j = 1
i = 1, j = 2
i = 1, j = 3
i = 2, j = 1
i = 3, j = 1
i = 3, j = 2
i = 3, j = 3
Branches 分支
if
语句和元素if-case
语句和元素switch
语句和表达式
你还可以使用以下方法在 Dart 中作控制流:
- Loops, like
for
andwhile
循环 ,如for
和while
- Exceptions, like
try
,catch
, andthrow
异常 ,如try
、catch
和throw
If 如果
Dart 支持带有可选 else
子句的 if
语句。if
后面括号中的条件必须是计算结果为布尔值的表达式:
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
If-case If 大小写
Dart if
语句支持 case
子句,后跟一个模式 :
if (pair case [int x, int y]) return Point(x, y);
如果模式与值匹配,则分支将使用模式在范围中定义的任何变量执行。
在前面的示例中,列表模式 [int x, int y]
与值对
匹配,因此分支返回 Point(x, y)
使用模式定义的变量 x
和 y
执行。
否则,控制流将前进到 else
分支执行(如果有):
if (pair case [int x, int y]) {
print('Was coordinate array $x,$y');
} else {
throw FormatException('Invalid coordinates.');
}
if-case 语句提供了一种将 针对单个模式进行 destructure 的解构。要针对多个模式测试值,请使用 switch。
版本提示
if 语句中的 Case 子句要求语言版本至少为 3.0。
Switch statements switch 语句
switch
语句根据一系列 case 评估值表达式。每个 case
子句都是要匹配的值的模式 。您可以为 case 使用任何类型的模式 。
当该值与案例的模式匹配时,将执行案例正文。非空大小写
子句在完成后跳转到 switch 的末尾。它们不需要 break
语句。结束非空 case
子句的其他有效方法是 continue、throw 或 return 语句。
当没有 case
子句匹配时,使用 default
或通配符 _ 子句执行代码:
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 共享一个 body。对于未落空的空 case,请使用 break 作为其主体。对于非顺序 fall-through,您可以使用 continue 语句和标签:
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.
}
您可以使用 logical-or 模式来允许 case 共享 body 或 guard。
Switch expressions Switch 表达式
switch
表达式根据表达式生成值 body 的 body 匹配。 你可以在 Dart 允许表达式的地方使用 switch 表达式, except 的表达式语句的开头。例如:
var x = switch (y) { ... };
print(switch (x) { ... });
return switch (x) { ... };
如果要在 expression 语句的开头使用 switch,请使用 switch 语句 。
Switch 表达式允许您重写 switch 语句 ,如下所示:
// 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');
}
转换为表达式 ,如下所示:
token = switch (charCode) {
slash || star || plus || minus => operator(charCode),
comma || semicolon => punctuation(charCode),
>= digit0 && <= digit9 => number(),
_ => throw FormatException('Invalid'),
};
switch
表达式的语法与 switch
语句语法不同:
- case 不以
case
关键字开头。 - 案例正文是单个表达式,而不是一系列语句。
- 每个 case 都必须有一个 body;空 case 没有隐式 fallin。
- 大小写模式使用
=>
而不是:
从它们的主体中分离出来。 - 大小写由
,
分隔(并且允许使用可选的尾随,
)。 - 默认情况只能使用
_
,而不允许同时使用default
和_
。
Exhaustiveness checking 穷举性检查
穷举性检查是一项功能,如果值可以输入 switch 但与任何情况都不匹配,则报告编译时错误。
// Non-exhaustive switch on bool?, missing case to match null possibility:
switch (nullableBool) {
case true:
print('yes');
case false:
print('no');
}
default case (default
或 _
) 涵盖可流经 switch 的所有可能值。这使得任何类型的 switch 都变得详尽无遗。
枚举和密封类型对于 switch 特别有用,因为即使没有默认情况,它们的可能值也是已知的并且完全可枚举。在类上使用 sealed 修饰符可在切换该类的子类型时启用穷举性检查:
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 Guard 子句
要在 case
子句后设置可选的 guard 子句,请使用关键字 when
。guard 子句可以跟在 if case
后面,也可以跟在 switch
语句和表达式后面。
// 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;
}
守卫在匹配后计算任意布尔表达式。这允许您添加有关是否应执行案例正文的进一步约束。当 guard 子句的计算结果为 false 时,执行将继续到下一个情况,而不是退出整个 switch。
Error handling 错误处理
Exceptions 异常
你的 Dart 代码可以抛出和捕获异常。异常是指示发生了意外情况的错误。如果未捕获到异常,则引发异常的 isolate 将挂起,并且通常会终止 isolate 及其程序。
与 Java 相比,Dart 的所有异常都是 unchecked 异常。方法不声明它们可能引发的异常,并且您不需要捕获任何异常。
Dart 提供 Exception 和 Error 类型,以及许多预定义的子类型。当然,你可以 定义您自己的异常。但是,Dart 程序可以抛出任何 非 null 对象(而不仅仅是 Exception 和 Error 对象)作为异常。
Throw 扔
下面是引发或引发异常的示例:
throw FormatException('Expected at least 1 section');
你也可以抛出任意对象:
throw 'Out of llamas!';
生产质量代码通常会引发实现 Error 或 Exception 的类型。
由于引发异常是一个表达式,因此可以在 => 语句中引发异常,也可以在允许表达式的任何其他位置引发异常:
void distanceTo(Point other) => throw UnimplementedError();
Catch 抓住
捕获或捕获异常会阻止异常传播(除非重新引发异常)。捕获异常将为您提供处理异常的机会:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
若要处理可能引发多种类型异常的代码,可以指定多个 catch 子句。与引发的对象类型匹配的第一个 catch 子句处理异常。如果 catch 子句未指定类型,则该子句可以处理任何类型的引发的对象:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
如前面的代码所示,您可以使用 on
和 catch
,也可以同时使用两者。当您需要指定异常类型时,请使用 on
。当异常处理程序需要 exception 对象时,请使用 catch
。
您可以为 catch()
指定一个或两个参数。第一个是引发的异常,第二个是堆栈跟踪(StackTrace 对象)。
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
若要部分处理异常,同时允许其传播,请使用 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 最后
要确保无论是否引发异常,某些代码都能运行,请使用 finally
子句。如果没有 catch
子句与异常匹配,则在 finally
子句运行后传播异常:
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
finally
子句在任何匹配的 catch
子句之后运行:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
Assert 断言
在开发过程中,使用 assert 语句 — assert(<condition>, <optionalMessage>);
— 如果布尔条件为 false,则中断正常执行。
// 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
的第二个参数 (可选使用尾随逗号 ):
assert(
urlString.startsWith('https'),
'URL ($urlString) should start with "https".',
);
assert
的第一个参数可以是 解析为布尔值。如果表达式的值 为 true,则断言成功并执行 继续。如果为 false,则断言失败,并且会出现异常(一个 AssertionError 的 AssertionError)被抛出。
- Flutter 在 debug 模式下启用断言。
- 仅限开发的工具,例如 webdev serve 通常默认启用断言。
- 一些工具,比如 dart run 和 dart compile js 通过命令行标志支持断言:
--enable-asserts
。
在生产代码中,将忽略断言,并且不会计算 assert
的参数。