[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

参考

相关文章
|
26天前
|
开发框架 Dart 前端开发
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。本文从 Flutter 简介、特点、开发环境搭建、应用架构、组件详解、路由管理、状态管理、与原生代码交互、性能优化、应用发布与部署及未来趋势等方面,全面解析 Flutter 技术,助你掌握这一前沿开发工具。
56 8
|
3月前
|
Dart
如何在 Flutter 项目中使用 Dart 语言?
如何在 Flutter 项目中使用 Dart 语言?
137 58
|
1月前
|
Dart
flutter dart mixin 姿势
flutter dart mixin 姿势
|
2月前
|
Dart 开发者 Windows
flutter:dart的学习
本文介绍了Dart语言的下载方法及基本使用,包括在Windows系统上和VSCode中的安装步骤,并展示了如何运行Dart代码。此外,还详细说明了Dart的基础语法、构造函数、泛型以及库的使用方法。文中通过示例代码解释了闭包、运算符等概念,并介绍了Dart的新特性如非空断言操作符和延迟初始化变量。最后,提供了添加第三方库依赖的方法。
34 12
|
2月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
91 7
|
4月前
|
Kubernetes Cloud Native 搜索推荐
探索云原生技术:Kubernetes入门与实践打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第31天】云原生技术正改变着应用开发和部署的方式。本文将带你了解云原生的基石——Kubernetes,通过实际的代码示例,从安装到部署一个简单的应用,让你迅速掌握Kubernetes的核心概念和操作方法。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你进入云原生世界的桥梁。
|
存储 Dart
[Flutter]足够入门的Dart语言系列之常见运算符或操作符
Dart中的运算符提供对数据操作和处理的能力,其中的算术运算符、逻辑运算符非常符合现实中的使用情况,其他不同的运算符则有着自己的操作逻辑...
643 0
[Flutter]足够入门的Dart语言系列之常见运算符或操作符
|
存储 缓存 Dart
[Flutter]足够入门的Dart语言系列之面向对象:类成员的可见性、常量和工厂构造函数详解
类和成员的可见性,这在基于库或包的开发中非常重要,Dart中提供了工厂构造函数,可以方便的实现单例、缓存实例对象、返回子类等,以及常量构造函数的使用......
309 0
[Flutter]足够入门的Dart语言系列之面向对象:类成员的可见性、常量和工厂构造函数详解
|
Dart
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
函数(Function)也被称为方法(Method)。其最直观的理解就是数据中的函数,比如y=f(x),在编程中,f对输入x进行处理,返回结果y,就是一个函数......
1061 0
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
|
7月前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)