c#异常处理核心是按需选配:能具体恢复才用catch(且须从具体到一般排序),仅需资源清理优先用using或try/finally,既需恢复又需清理才用完整结构;空catch或盲目捕获exception比不处理更危险。

直接说结论:C# 中的 try/catch/finally 不是“加了就安全”,而是必须按场景选配、按类型分层、按资源生命周期兜底。捕获 Exception 或空 catch 块,比不写异常处理更危险。
什么时候该用 try/catch,而不是 try/finally
核心判断依据是:你是否能对这个错误做**具体恢复动作**。
- 需要恢复:比如读配置失败后 fallback 到默认值、网络超时后重试一次、
FileNotFoundException时提示用户选择新路径——这时必须配catch,且只捕获那个具体异常类型 - 只需清理:比如打开文件流、获取数据库连接、分配非托管内存——这些不管成功失败都得释放,用
try/finally就够;现代写法更推荐using(它底层就是try/finally) - 既需恢复又需清理:才上完整的
try/catch/finally,但注意finally里别再抛新异常,否则会吞掉原异常
catch 块顺序和类型为什么不能乱写
CLR 匹配 catch 是从上到下线性扫描,一旦命中就停止。如果把基类放前面,派生类永远没机会执行。
- 错误写法:
catch (Exception ex)写在最前 → 所有异常都被它吃掉,catch (NullReferenceException)根本不会进 - 正确顺序:从最具体到最宽泛,例如:
catch (DivideByZeroException)→catch (ArgumentException)→ (可选)catch (Exception),且末尾必须throw向上传递 - 特别注意:
catch不带类型(即catch { })等价于catch (Exception),但更隐蔽,绝对禁用
异步方法里 try/catch 容易漏掉什么
async 方法中 await 后的异常,会包装成 AggregateException 吗?不会。.NET 4.5+ 已保证单个 await 抛出的异常就是原始异常类型,但有三个关键细节:
-
async void方法里的异常无法被外层捕获,会直接炸进程,务必改用async Task - 多个
await并发执行时(如Task.WhenAll),失败会聚合成AggregateException,需检查.InnerExceptions -
finally在 async 方法中仍 100% 执行,但注意:它运行在线程池线程上,不是原始上下文;若依赖 UI 控件,得手动切回(比如用Dispatcher.Invoke)
using 和 finally 谁更适合资源释放
绝大多数情况,using 是首选,它语义清晰、编译器保障、无缩进嵌套。但有两个例外必须手写 finally:
- 资源变量在
try块外声明(比如因条件逻辑延迟初始化),此时using语法不适用 - 需要在释放前做状态判断(例如:仅当连接处于
Open状态才调用Close()),finally里可加 if 分支,而using会无条件调用Dispose() - 注意:
using编译后就是try/finally,所以它里面的Dispose()抛异常也会中断流程,建议Dispose()方法内部自行 try/catch
最常被忽略的一点:异常过滤器(exception filters)在 C# 6+ 支持 catch (IOException ex) when (ex.HResult == -2147024894) 这种精准拦截,比在 catch 块里 if 判断更安全——它不会重置堆栈,调试时能看到原始异常位置。











