HoRain云--Dart异常处理全攻略:从入门到精通 HoRain云小助手个人主页 个人专栏: 《Linux 系列教程》《c语言教程》⛺️生活的理想就是为了理想的生活!⛳️ 推荐前些天发现了一个超棒的服务器购买网站性价比超高大内存超划算忍不住分享一下给大家。点击跳转到网站。专栏介绍专栏名称专栏介绍《C语言》本专栏主要撰写C干货内容和编程技巧让大家从底层了解C把更多的知识由抽象到简单通俗易懂。《网络协议》本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘一起解密网络协议在运行中协议的基本运行机制《docker容器精解篇》全面深入解析 docker 容器从基础到进阶涵盖原理、操作、实践案例助您精通 docker。《linux系列》本专栏主要撰写Linux干货内容从基础到进阶知识由抽象到简单通俗易懂帮你从新手小白到扫地僧。《python 系列》本专栏着重撰写Python相关的干货内容与编程技巧助力大家从底层去认识Python将更多复杂的知识由抽象转化为简单易懂的内容。《试题库》本专栏主要是发布一些考试和练习题库涵盖软考、HCIE、HRCE、CCNA等目录⛳️ 推荐专栏介绍抛出与捕获基础多个 catch 子句自上而下匹配finally无论成败都执行rethrow保留原始堆栈的再抛出自定义异常与 Error 的区分异步代码中的异常处理Dart 2.16 推荐Error.throwWithStackTrace一个完整的处理范式Dart 的异常处理围绕throw—try—catch—finally​ 这套结构化机制展开。Dart 中任何非 null 对象都可以被抛出——不仅限于ExceptionError乃至普通对象都能 throwthrow本身是个表达式所以可以直接写在函数体里void distanceTo(Point other) throw UnimplementedError();。下面按抛出 → 捕获 → 清理 → 重抛/包装的顺序过一遍。抛出与捕获基础try { breedMoreLlamas(); } on OutOfLlamasException { // 只关心类型不需要异常对象 buyMoreLlamas(); }on和catch可以单独用也可以组合只用on Type只想针对某类异常做处理不需要拿到异常对象只用catch (e)需要异常对象但不关心类型on Type catch (e, s)既要类型匹配又要异常对象和堆栈catch的第二个参数s是StackTrace对象排查线上问题非常关键。多个 catch 子句自上而下匹配Dart 允许多个on/catch子句第一个匹配到类型的子句生效后续忽略。所以具体类型要写在前面宽泛的写在后面try { breedMoreLlamas(); } on OutOfLlamasException { // 特定异常优先处理 buyMoreLlamas(); } on Exception catch (e) { // 其他 Exception print(Unknown exception: $e); } catch (e) { // 没有指定类型 兜底能捕获一切包括 Error print(Something really unknown: $e); }⚠️ 兜底catch (e)会连Error一起吞掉。Effective Dart 明确建议大多数情况下应该捕获Exception而不是所有类型Exception是所有运行时异常的基类它排除了代表代码 bug 的Error。finally无论成败都执行资源清理、状态复位等必须跑的逻辑放在finallytry { breedMoreLlamas(); } catch (e) { print(Error: $e); // 先处理异常 } finally { cleanLlamaStalls(); // 再清理 }如果try中没有catch匹配到异常finally执行完后异常会继续向上传播。rethrow保留原始堆栈的再抛出这是 Dart 异常处理里最容易被误用的一点。看两段代码的区别// ❌ 错误示范throw e 会重置堆栈到当前位置 try { somethingRisky(); } catch (e) { if (!canHandle(e)) throw e; // 堆栈从这里重新开始 handle(e); } // ✅ 正确做法rethrow 保留原始堆栈 try { somethingRisky(); } catch (e) { if (!canHandle(e)) rethrow; // 堆栈信息完整保留 handle(e); }rethrow只能在catch块内使用它会把异常原封不动含原始堆栈继续向上抛。Effective Dart 专门有一条 lint 规则use_rethrow_when_possible来强制这一点。自定义异常与 Error 的区分Dart 里有两个重要的基类语义不同类型用途示例Exception运行时失败不代表代码 bug​FormatException、IOExceptionError程序错误意味着代码有 bugArgumentError、StateError、OutOfMemoryError指导原则你的 API 想报告调用方用错了 → 抛Error子类如ArgumentError运行时失败、不代表 bug → 抛Exception或其子类不要​catch具体的Error类型lint 规则avoid_catching_errors因为Error应该一路冒泡直到终止程序并打印堆栈让你定位修复 bug捕获它会掩盖问题自定义异常很简单实现Exception接口即可class OutOfLlamasException implements Exception { final String message; OutOfLlamasException(this.message); override String toString() OutOfLlamasException: $message; }异步代码中的异常处理try-catch在async函数中对同步和异步代码同样有效直接await可能抛出的异步操作即可Future describeFlybyObjects(List flybyObjects) async { try { for (final object in flybyObjects) { var description await File($object.txt).readAsString(); print(description); } } on IOException catch (e) { print(Could not describe object: $e); } finally { flybyObjects.clear(); } }如果是即发即弃的Future且你确实不在乎成功与否可以调用.ignore()FirebaseAnalytics.instance.logAppOpen().ignore();Dart 2.16 推荐Error.throwWithStackTrace有时候你需要在catch块里抛出一个新的异常比如把底层异常包装成业务异常但又不想丢失原始堆栈。这时候直接用throw NewException()会丢失原堆栈——Dart 2.16 引入的Error.throwWithStackTrace就是为解决这个问题try { final json await client.get(id); return Item.fromJson(json); } on Object catch (_, stackTrace) { Error.throwWithStackTrace( ApiException(Something goes wrong), stackTrace, ); }它的作用是带着原始的stackTrace​ 抛出新的错误。如果error继承自Error且之前未被抛出过其Error.stackTrace属性会被设为传入的stackTrace。相应的DCM 等静态分析工具提供了avoid_throw_in_catch_block规则专门警告在 catch 块内直接throw会丢失原堆栈的反模式。 简单记想原样继续抛 →rethrow想换成新异常但保留原堆栈 →Error.throwWithStackTrace。一个完整的处理范式综合上面所有要点这是 Dart 中处理异常的标准范式FutureItem fetchItem(String id) async { try { final json await client.get(id); return Item.fromJson(json); } on FormatException { // 可预见的、可恢复的异常 → 返回默认值或特殊标记 return const Item.empty(); } on TimeoutException catch (e, s) { // 可预见但需要包装的异常 → 保留堆栈 Error.throwWithStackTrace(ApiException(请求超时), s); } catch (e, s) { // 兜底记录日志后继续向上抛绝不静默吞掉 log.error(fetchItem failed: $e, stackTrace: s); rethrow; } finally { client.close(); } }几个必须遵守的实践准则绝不静默丢弃异常——on Object { return null; }是最糟糕的写法丢失堆栈后在 Crashlytics/Sentry 里几乎无法定位问题优先捕获具体类型不要用裸catch (e)一把梭不要捕获具体的Error子类让 bug 类错误自然冒泡重新抛出用rethrow包装异常用Error.throwWithStackTrace全局异步异常可以用runZonedGuarded兜住runZonedGuarded(() async { longAsyncOperation(); runApp(App()); }, (error, stackTrace) /* 上报 */);异常处理看似简单但在 Dart 里堆栈信息的保留是核心痛点——throw和rethrow一字之差线上排查难度天差地别。把rethrow和Error.throwWithStackTrace这两个工具用熟你的错误处理就达到了 Dart 社区的生产级标准。需要我再展开讲讲Future与Stream的异常处理差异还是Result类型函数式错误处理的替代方案❤️❤️❤️本人水平有限如有纰漏欢迎各位大佬评论批评指正如果觉得这篇文对你有帮助的话也请给个点赞、收藏下吧非常感谢! Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧