1. Dart流程控制语句概述
在Dart编程语言中,流程控制语句是构建程序逻辑的基础骨架。就像建筑师需要钢筋来搭建房屋结构一样,程序员需要流程控制来组织代码执行顺序。Dart提供了与其他C风格语言类似的流程控制结构,但也有一些独特的语法糖和特性值得关注。
我刚开始接触Dart时,发现它的流程控制既熟悉又陌生。熟悉的是if-else、for循环这些经典结构,陌生的是像for-in循环、switch表达式这些更现代化的语法。经过多个Flutter项目实战后,我总结出Dart流程控制最核心的三类语句:条件分支、循环迭代和跳转语句。
2. 条件分支语句详解
2.1 if-else语句
Dart中的if语句与其他语言几乎相同,但布尔条件必须严格返回bool类型。这点与JavaScript不同,后者会进行隐式类型转换。例如:
dart复制int age = 18;
if (age >= 18) { // 正确:条件表达式返回bool
print('已成年');
} else {
print('未成年');
}
// 错误示例:Dart不会将非bool值转为bool
if (1) { // 编译错误
print('这行不通');
}
实际项目中,我经常使用级联的if-else if来处理多条件分支。但要注意,当分支超过3层时,建议考虑改用switch语句或策略模式,以提高代码可读性。
2.2 switch语句
Dart的switch比许多语言更强大,主要体现在:
- 支持字符串和编译时常量比较
- 每个case不需要break(默认不穿透)
- 支持空安全
典型用法:
dart复制String command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
break; // 虽然Dart不强制,但显式break更清晰
case 'CLOSE':
executeClose();
continue nowClosed; // 使用continue跳转标签
nowClosed:
case 'SHUT':
print('已关闭');
break;
default:
handleUnknown();
}
在Flutter开发中,我常用switch处理路由命令或状态枚举。一个实用技巧是:对于枚举值,Dart 2.15+支持增强的switch表达式语法:
dart复制enum Status { idle, loading, success, error }
String statusMessage(Status status) => switch (status) {
Status.idle => '准备就绪',
Status.loading => '加载中...',
_ => '状态:${status.name}' // 默认分支
};
3. 循环迭代语句解析
3.1 基础循环结构
Dart提供三种标准循环:
-
for循环:精确控制迭代次数
dart复制for (int i = 0; i < 5; i++) { print('第$i次循环'); } -
while循环:条件前置
dart复制while (!isDone) { doSomething(); } -
do-while循环:条件后置(至少执行一次)
dart复制do { requestConfirmation(); } while (!confirmed);
在性能敏感场景中,for循环通常最快,因为循环变量在栈上分配。而集合遍历更推荐使用for-in或高阶函数。
3.2 集合遍历的多种方式
Dart为集合遍历提供了丰富的语法:
dart复制var numbers = [1, 2, 3];
// 传统for-in
for (var n in numbers) {
print(n * 2);
}
// forEach方法
numbers.forEach((n) => print(n));
// 带索引的遍历
for (var i = 0; i < numbers.length; i++) {
print('$i: ${numbers[i]}');
}
// 使用扩展运算符的简洁写法
numbers.map((n) => n * 2).forEach(print);
在Flutter开发中,我总结的经验法则是:
- 需要索引时用传统for循环
- 仅需元素时用for-in
- 链式操作使用map/where等集合方法
3.3 循环控制关键字
Dart提供两个关键控制语句:
-
break:立即退出当前循环
dart复制while (true) { if (timeOut()) break; // 满足条件时跳出 keepTrying(); } -
continue:跳过当前迭代
dart复制for (var i = 0; i < 10; i++) { if (i.isOdd) continue; // 跳过奇数 print(i); // 只打印偶数 }
在复杂循环中,可以为循环添加标签实现更精确的控制:
dart复制outerLoop: for (var x in ['A', 'B', 'C']) {
for (var y in [1, 2, 3]) {
if (y == 2) break outerLoop; // 直接跳出外层循环
print('$x$y');
}
}
4. 异常处理流程控制
4.1 try-catch-finally结构
Dart使用异常来处理错误流程,基本结构如下:
dart复制try {
riskyOperation();
} on SpecificException catch (e) {
handleError(e);
} catch (e, s) { // s是堆栈跟踪
logError(e, s);
} finally {
cleanup(); // 无论是否异常都会执行
}
几个关键点:
- 使用
on捕获特定类型异常 - 可以获取堆栈跟踪用于调试
- finally块常用于资源清理
4.2 抛出异常的最佳实践
在Dart中抛出异常使用throw关键字:
dart复制class ValidationError implements Exception {
final String message;
ValidationError(this.message);
}
void validate(String input) {
if (input.isEmpty) {
throw ValidationError('输入不能为空');
}
}
我的经验是:
- 优先使用Dart内置异常类型(如ArgumentError)
- 自定义异常应实现Exception接口
- 错误信息应该足够详细
5. 现代Dart流程控制技巧
5.1 集合的if和for
Dart允许在集合字面量中使用控制流:
dart复制var list = [
'首页',
if (isLoggedIn) '个人中心', // 条件插入
for (var i in [1, 2, 3]) '项目$i' // 循环展开
];
这在构建UI配置列表时特别有用:
dart复制var menuItems = [
const PopupMenuItem(value: 'home', child: Text('首页')),
if (kDebugMode) // 只在调试模式显示
const PopupMenuItem(value: 'debug', child: Text('调试工具')),
for (var lang in supportedLanguages)
PopupMenuItem(value: lang, child: Text('切换为$lang')),
];
5.2 空安全与流程控制
Dart的空安全特性与流程控制紧密结合:
dart复制String? maybeString;
// 传统null检查
if (maybeString != null) {
print(maybeString.length); // 自动提升为非空
}
// 或使用空断言操作符
print(maybeString?.length ?? 0);
// 在switch中处理null
switch (maybeString) {
case null:
handleNull();
break;
case 'special':
handleSpecial();
break;
default:
handleString(maybeString); // 自动非空
}
5.3 异步流程控制
Dart的async/await让异步代码像同步一样清晰:
dart复制Future<void> fetchData() async {
try {
showLoading();
var data = await api.fetch(); // 等待异步结果
updateUI(data);
} catch (e) {
showError(e);
} finally {
hideLoading();
}
}
对于多个异步任务,可以使用Future.wait:
dart复制var results = await Future.wait([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
6. 常见问题与调试技巧
6.1 流程控制中的典型错误
-
无限循环:忘记更新循环变量
dart复制// 错误示例 var i = 0; while (i < 10) { print(i); // 忘记i++ } -
case穿透:虽然Dart默认不穿透,但显式break是好习惯
-
类型比较:使用
==比较对象时确保实现了==和hashCode
6.2 调试流程控制语句
- 使用
print()或log()输出中间值 - 在IDE中设置断点逐步执行
- 对于复杂条件,可以拆分为多个临时变量
dart复制// 难以调试的复杂条件
if (user != null && user.age > 18 && !user.isBanned || isAdmin) {...}
// 改为
final isAdult = user?.age > 18;
final isEligible = user != null && isAdult && !user.isBanned;
if (isEligible || isAdmin) {...}
6.3 性能优化建议
- 在热循环中避免创建临时对象
- 对于大型集合,考虑使用
iterator手动遍历 - 多次使用的条件结果应该缓存
dart复制// 低效
while (expensiveCheck()) {...}
// 优化
bool result = expensiveCheck();
while (result) {
...
result = expensiveCheck();
}
掌握Dart流程控制的关键在于理解每种结构的适用场景。经过多个项目的实践,我发现:简单的条件用if-else,多分支枚举用switch,集合处理多用高阶函数,异步流程善用await,这样写出的代码既高效又易于维护。