flutter的循环和分支以及错误处理

Loops  循环

  • for loops  for 循环
  • while and do while loops
    while 和 do while 循环
  • break and continue
    中断并继续

你还可以使用以下方法在 Dart 中作控制流:

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 中作控制流:

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 子句的其他有效方法是 continuethrow 或 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)被抛出。

在生产代码中,将忽略断言,并且不会计算 assert 的参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值