[Flutter]足够入门的Dart语言系列之流程控制语句:中断和异常(continue/break、try...catch)

简介: 循环的执行是通过循环条件来控制的,但是,有时我们想要通过额外的条件判断,来决定是否中断执行,或者中断本次循环而继续执行下次及之后的循环;此外,我们需要对于异常情况的处理...
+关注继续查看

中断流程

循环的执行是通过循环条件来控制的,但是,有时我们想要通过额外的条件判断,来决定是否中断执行,或者中断本次循环而继续执行下次及之后的循环。

这就引出了continuebreakreturn关键字。

return 不仅用于循环的终止,其主要用于函数的返回,或者,整个代码执行的返回(不执行后续代码)。

continue跳转执行下一次的循环

continue表示中止本次循环,继续之后的循环处理。

下面的示例,当i为偶数时,则不执行后面的代码,而是结束当前循环,跳到下一次的循环处理。

i.isEven 判断是否是偶数
int sum = 0;
for (int i = 0; i < 10; i++) {
  if(i.isEven){// 如果是偶数
    continue; // 直接跳转到下一次循环
  }
  sum += i; 
  print("第 $i 次循环,计入 sum");
}
print("sum: $sum");

上面用于在偶数时不执行后面的循环体,而是跳到下一次循环,最终输出如下结果:

第 1 次循环,计入 sum
第 3 次循环,计入 sum
第 5 次循环,计入 sum
第 7 次循环,计入 sum
第 9 次循环,计入 sum
sum: 25

break结束整个循环

break用来结束整个循环。是在循环条件之外结束循环的方式。

比如,在一个While(true)循环中,当条件满足时执行break结束循环,否则会一直死循环下去。

var sum = 1;
var count = 0;
while (true) {
  if (sum > 100) {
    break;
  }
  sum += sum;
  count++;
}

print("sum是:$sum");
print("加总的次数为:$count");

输出结果:

sum是:128
加总的次数为:7

return当前函数

return主要用于返回当前函数,表示之后的代码均不执行,而是返回(通常会返回数据)。如果用在循环中,起到结束循环、整个代码返回作用。

比如,还是上一次的循环,将终止条件改为return,后续的代码将不会执行,产生Dead code

var sum = 1;
var count = 0;
while (true) {
  if (sum > 100) {
    return;
  }
  sum += sum;
  count++;
}

print("sum是:$sum");
print("加总的次数为:$count");

异常流程

异常是相对于正常处理流程中出现的意外,通常是可预见的,比如网络异常、文件读取异常、日期格式化异常、数值越界异常、数据值异常等。

发生异常,如果不处理,整个执行流程(或程序)就有可能因错误而停止或崩溃,无法执行之后的任务。

异常捕获就是对可能出现的错误进行捕捉,然后处理或记录异常情况,保证程序后续的执行。同时,异常信息的记录,可以更容易的定位问题的发生原因。

异常流程通常用try...catch...捕获异常,来处理对应的错误。

Dart的所有异常都是非必检异常,除了自定义异常,Dart中可以将任何非null对象作为异常抛出(通常不推荐)。

异常的捕获

如下,在task1方法中,将参数转换为int类型,但是由于传入的字符串不能转换为数字,将会发生转换异常。

void main(){
  task1('a');
  task2();
}

int task1(String num){
  return int.parse(num);
}

void task2(){
  print("Task2");
}

执行时会产生未捕获或未处理的异常:

Uncaught Error: FormatException: a

使用try...catch...捕获异常,catch可以获取异常回调的两个参数。

  • e 表示异常对象,此处为 FormatException
  • s 是 _StackTrace 对象,用于记录异常的栈信息

如下,捕获异常并输出异常类型和异常的堆栈信息:

void main(){
  try{
    task1('a');
  }catch(e,s){
    print("${e.runtimeType}: ${e.toString()}"); 
    print("${s.runtimeType}: ${s.toString()}"); 
  }
  task2(); // Task2
}

int task1(String num){
  return int.parse(num);
}

void task2(){
  print("Task2");
}

结果如下。通过 e 可以看出异常的信息,通过 s 可以看出发生错误时方法栈的情况,便于快速定位到异常发生的地点。

FormatException: FormatException: a
_StackTrace: FormatException: a
    at Object.wrapException (<anonymous>:344:17)
    at Object.int_parse (<anonymous>:2438:15)
    at main (<anonymous>:2595:11)
    at <anonymous>:3217:7
    at <anonymous>:3200:7
    at dartProgram (<anonymous>:3211:5)
    at <anonymous>:3219:3
    at replaceJavaScript (https://www.dartpad.dev/scripts/frame.js:19:19)
    at messageHandler (https://www.dartpad.dev/scripts/frame.js:80:13)
Task2

由于捕获了错误,之后task2();继续执行。

抛出异常

与异常捕获同样重要的是“抛出异常”。

在编写很多方法或类库时,如果遇到不符合条件的情况,我们需要抛出异常,即停止程序的执行,或者交由外部代码来处理异常的情况。

异常抛出,通常都是遇到代码无法执行的情况时,以异常的形式告知停止代码的执行。需要处理修正异常情况后,才能真正的执行。

如下,在函数GetMyDate中,如果有问题,抛出throw FormatException异常:

void main(){
  GetMyDate('2022-10-10');

  GetMyDate('2022-10');
}

List<String> GetMyDate(String myDate){
  var result=myDate.split('-');
  if(result.length!=3){
    throw FormatException('格式不正确,日期字符串必须为`yyyy-(M)M-(d)d`');
  }
  return result;
}

dart中除数为0不会报错

如下,除以0时:

var a=0;
var b=10;
var c=b/a;

print(c); // Infinity

和js中一样,除数为0时不会报错,其结果为Infinity

自定义异常与捕获特定类型的异常

异常的顶层抽象类是 Exception ,通过其 factory 构造可以创建Exception 对象,本质上创建的运行时类型是 _Exception ,可传入一个对象用于表示异常信息。

以上面的除数为0作为示例,我们在除法计算中,判断如果除数为0,则抛出一个异常:

double DivideMethod(int a,int b){
  if(b==0){
    throw Exception('除数不能为0');
  }
  return a/b;
}

以上的抛出异常都是使用的Dart自带异常对象,有时我们需要自定义不同的异常,比如自定义一个除数异常DivideException

double DivideMethod(int a,int b){
  if(b==0){
    throw DivideException(b);
  }
  return a/b;
}

class DivideException implements Exception{
  final int arg;

  DivideException(this.arg);

  @override
  String toString() => "除数不能为$arg";
}

测试,并捕获特定的DivideException

try{
  DivideMethod(10, 0);
}
on DivideException catch(e,s){
  print("${e.runtimeType}: ${e.toString()}");
  print("${s.runtimeType}: ${s.toString()}");
} catch (e,s) {
  // 其余异常处理
}

输出:

DivideException: 除数不能为0
_StackTrace: #0      DivideMethod (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test.dart:30:5)
#1      main (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test.dart:11:5)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
on TypeException catch 用于捕获特定类型TypeException的异常。

仅捕获异常,而不获取异常参数。

try{
  DivideMethod(10, 0);
}
on DivideException {
  print("发生了异常");
} catch (e) {
  // 其余异常处理
}

rethrow将捕获的异常再次抛出。

finally语句

无论是否抛出异常,finally语句始终执行,通常finally放在catch语句之后。用于处理一些必须的清理操作,不管是否有异常,均会被执行。

try{
  DivideMethod(10, 0);
} catch (e) {
  // 异常处理
}
finally{
  print('执行必需的清理操作');
}

如果没有指定 catch 语句来捕获异常,则异常会在执行完 finally 语句后抛出:

try{
  DivideMethod(10, 0);
} finally{
  print('执行必需的清理操作');
}

关于Error类型

在Dart中,除了抛出Exception类型外,还可以抛出Error类型(及其子类型),其更多的表示程序运行的失败,这些失败应该被避免。

比如最常见的参数不为空的错误:

throw ArgumentError.notNull('参数不为空的Error');

输出:

Unhandled exception:
Invalid argument(s) (参数不为空的Error): Must not be null
#0      test (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test2.dart:27:5)
#1      main (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test2.dart:21:3)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

或,其他的参数错误,打印出错误相关的值:

throw ArgumentError.value(i,'参数不能小于0');
Unhandled exception:
Invalid argument (参数不能小于0): -1

参考

相关文章
|
6天前
|
Dart 开发者
Flutter入门之Dart中的并发编程、异步和事件驱动详解
Flutter入门之Dart中的并发编程、异步和事件驱动详解 Dart是一种高效、快速、灵活且用于Web和移动应用程序开发的编程语言。在Dart中,支持并发编程、异步和事件驱动等特性,这些特性使得Dart在处理诸如网络请求、文件I/O、用户输入等方面表现出色。本文将详细介绍Dart中的这些特性。
|
1月前
|
Dart
通义灵码请问啥时候支持flutter或dart
通义灵码请问啥时候支持flutter或dart
185 1
|
2月前
|
Dart API Android开发
带你读《深入浅出Dart》二十二、Flutter简介
带你读《深入浅出Dart》二十二、Flutter简介
|
2月前
|
Dart 开发工具 iOS开发
带你读《深入浅出Dart》二十三、Flutter环境的安装与配置(1)
带你读《深入浅出Dart》二十三、Flutter环境的安装与配置(1)
|
2月前
|
Dart 开发工具 Android开发
带你读《深入浅出Dart》二十三、Flutter环境的安装与配置(2)
带你读《深入浅出Dart》二十三、Flutter环境的安装与配置(2)
|
2月前
|
Dart C++
带你读《深入浅出Dart》二十四、编写第一个Flutter应用
带你读《深入浅出Dart》二十四、编写第一个Flutter应用
|
2月前
|
Dart Android开发 UED
带你读《深入浅出Dart》二十七、Flutter路由管理
带你读《深入浅出Dart》二十七、Flutter路由管理
|
2月前
|
Dart API
带你读《深入浅出Dart》二十九、Flutter网络请求(1)
带你读《深入浅出Dart》二十九、Flutter网络请求(1)
|
2月前
|
XML JSON Dart
带你读《深入浅出Dart》二十九、Flutter网络请求(2)
带你读《深入浅出Dart》二十九、Flutter网络请求(2)
|
2月前
|
Dart
带你读《深入浅出Dart》三十、Flutter实战之TODO应用(1)
带你读《深入浅出Dart》三十、Flutter实战之TODO应用(1)
相关产品
云迁移中心
推荐文章
更多