Dart语言入门 - 流程控制详解(新手指南:避坑大全)
⚠️ 警示区:这里藏着你可能在实战中踩过的80%流程控制相关的坑!包括[漏写break导致错误贯穿][空循环分号陷阱]等高频错误
目录
1. 条件判断的隐藏规则
必须明确的布尔条件(与JS不同!)
dart
var name = '';
if (name.isEmpty) { /* 正确方式 */ }
// if (name) { ... } ❌ 无法编译!非布尔类型不能直接作为条件else if 的实质
dart
// 以下代码完全等效 → 掌握语法糖原理
if(a) {...}
else if(b) {...}
// 等价于
if(a) {...}
else {
if(b) {...}
}三元运算符类型推断陷阱
dart
var result = condition ? 1 : 'error'; // 类型变成 Object → 要明确类型声明
↓ ↓ ↓ 修改方案 ↓ ↓ ↓
num result = condition ? 1 : 0; // 强制保持统一类型2. 循环控制的深度机制
for循环的作用域剖析
dart
for (var i = 0; i < 5; i++) {
print(i);
}
print(i); // ❌ 报错!i的作用域限定在循环体内(Dart 2.15+ 行为变化)While与do-while本质区别
dart
int counter = 5;
while (counter < 5) { ... } // 0次执行 → 先判断
do { ... } while(counter <5) // 1次执行 → 后判断死循环的正确写法
dart
// 最安全的形式 → 可配合break退出
while (true) {
if (exitCondition) break;
}for-in循环隐藏要求
dart
var numbers = [1,2,3];
// 等同于:var iterator = numbers.iterator;
for (var num in numbers) {
print(num);
}
// 不能直接在普通Object上使用:
var myObject = MyCustomClass(); // 必须实现 Iterable💥 forEach的局限性:
dart
// 无法用break/continue控制流程 → 全程执行所有元素
list.forEach((item) {
if (item == 2) return; // 相当于continue,无法break
});3. Switch语句的雷区
必须遵守的黄金法则
dart
String fruit = 'apple';
switch (fruit) {
case 'apple':
print('🍎');
// ❌ 若此处漏写break → 会发生case贯穿直到遇到break
break;
case 'banana':
print('🍌');
return; // 可用return取代break
default:
print('未知水果');
}高级匹配模式
dart
// List模式匹配(新手极少用但非常实用)
var list = [1,2];
switch (list) {
case [int a, int b]:
print('包含两个整数');
break;
case [_, ...]:
print('长度>=1');
}when与switch的选择
dart
// Dart原生没有when语法 → 可用带条件判断的case替代
switch (true) {
case (score >= 90):
print('优秀');
break;
case (score >= 60):
print('及格');
}4. 异常处理核心技巧
完整的异常处理结构
dart
try {
// 可能抛出异常的代码
} on FormatException catch(e) { // 具体类型捕获
print('格式错误:$e');
} on IOException { // 可省略异常对象
print('IO异常');
} catch (e, s) { // 捕获所有异常 + 堆栈跟踪
print('未知错误:$e');
print('堆栈:$s');
} finally { // 必定执行的区域
cleanUpResources();
}重要注意事项
- finally总是在return前执行:
dart
String test() {
try {
return '结果';
} finally {
print('finally执行');
}
} // 输出顺序:finally执行 → 返回'结果'- 避免在catch块中忘记处理异常:
dart
// bad:
catch (e) {
print(e); // 仅打印不够,需考虑是否需要重新抛出
}
// good:
catch (e) {
logError(e); // 记录日志
rethrow; // 或做其他处理
}5. 流程中断特殊用法
循环标签的使用
dart
outerLoop:
for (var i = 1; i <= 3; i++) {
innerLoop:
for (var j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outerLoop; // 直接跳出外层循环
}
}
}断言(assert)的适用场景
dart
// 仅用于开发调试,生产环境自动忽略
assert(num > 0, '数字必须为正数'); // 第二个参数是可选消息Continue的灵活应用
dart
// 可指定跳转到指定标签的循环层次
list.forEach((number) {
if (number == 2) continue;
print(number);
});综合应用案例
案例1:分数等级判断
dart
String getGrade(int score) {
switch (score ~/ 10) { // 整除操作符
case 10: case 9: return 'A';
case 8: return 'B';
case 7: return 'C';
case 6: return 'D';
default:
if(score < 0) throw ArgumentError('分数不能为负');
return 'E';
}
}案例2:安全解析嵌套数据
dart
dynamic jsonData = fetchData();
try {
var userName = jsonData['user']['profile']['name'] as String;
} on NoSuchMethodError {
print('数据路径错误');
} on TypeError catch(e) {
print('类型转换失败:${e.toString()}');
}错误排查清单
- 死循环:
- 检查循环条件是否永远不会变为false
- 验证循环体内是否没有改变条件的操作
- 预期之外的case穿透:
- 每个case的最后是否有break/return/throw
- 是否需要故意穿透(这种情况需注释说明)
- 异常未被捕获:
- 是否所有可能的异常都被try覆盖
- 类层次结构中最具体的异常应放在前面
- 异步流程失控:
- 在async函数中的循环体内是否需要await
- 避免在forEach中直接使用async函数(大部分情况无法正常等待)
💡 最佳实践总结:
- Switch语句如无必要阻止贯穿时必须写上注释说明
- 高危操作(网络请求/文件操作)必须使用异常捕获
- 尽可能用break配合标签替代复杂的嵌套条件判断
- 迭代集合时优先考虑for-in而不是forEach(除非确定无需控制流程)