应避免在 catch 中仅记录日志而不重抛异常,否则会截断调用栈并导致静默失败;正确做法是使用 throw; 保留堆栈,或 throw new CustomException("msg", ex) 封装异常,禁用 throw ex;。

catch 里不写 throw 或 throw ex 会吞掉异常
很多开发者在 catch 块里只做日志就完事,比如:
try { DoSomething(); }
catch (Exception ex) {
Log.Error(ex.Message);
}这会导致调用栈被截断,上层完全不知道发生了什么。更糟的是,如果后续逻辑依赖异常传播(比如事务回滚、重试机制),程序会静默失败。
正确做法是:需要处理就 throw;(原异常重抛),需要包装就 throw new CustomException("msg", ex);。绝对避免 throw ex;——它会清空原始堆栈信息。
-
throw;:保留原始堆栈,推荐用于“记录后继续上抛” -
throw new Exception(..., ex);:带内嵌异常,适合封装领域错误 -
throw ex;:删掉堆栈,调试时找不到源头,禁用
finally 不保证执行,但 try/catch/finally 结构本身是安全的
finally 块在绝大多数情况下都会运行,包括 return、break、continue 甚至 throw 出当前方法时。但它不是绝对可靠的——比如进程被强制终止(Environment.FailFast、Thread.Abort 已废弃但仍有类似场景)、StackOverflow、OutOfMemory,或 Windows 上的硬关机。
所以 finally 适合做资源清理(如 stream.Close()、conn.Dispose()),但不适合放关键业务逻辑(比如发通知、写审计日志)——这些应该放在 try 或 catch 中并单独容错。
- 用
using替代手动finally关闭资源,更简洁且编译器保障 - 如果必须手写
finally,优先调用Dispose()而非Close()(后者可能不释放所有资源) - 不要在
finally里写可能抛异常的代码,否则会覆盖原异常
多个 catch 块顺序错乱会导致子类异常永远捕不到
C# 的 catch 是从上到下匹配的,一旦某个 catch 满足类型条件就进入,不再检查后面的。所以如果把基类异常(如 Exception)写在子类(如 ArgumentNullException)前面,子类永远没机会触发。
try { ... }
catch (Exception ex) { /* 这里会捕获所有异常 */ }
catch (ArgumentNullException ex) { /* 永远不会进来 */ }编译器其实会报错:CS0160 “先前已捕获到一个更加具体的异常类型”,但有些旧项目或动态编译场景可能绕过检查。
- 总是按“从具体到宽泛”排列:先
ArgumentNullException,再IOException,最后Exception - 不要为了“省事”只留一个
catch (Exception),丢失异常语义 - 如果想统一处理,用
catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException)来组合条件
async 方法里 try/catch 不能直接捕获 await 后的异常
在 async Task 方法中,await 后抛出的异常会被包装进 Task,不会直接冒泡到同步的 catch 块——除非你 await 它。下面这段代码里的 catch 根本抓不到 DoAsyncWork() 抛的异常:
try {
DoAsyncWork(); // 忘了 await!返回 Task 后立即往下走
}
catch (Exception ex) { /* 不会执行 */ }正确写法必须 await:
try {
await DoAsyncWork();
}
catch (HttpRequestException ex) { /* 这里才能捕获 */ }- 未
await的异步调用等于“fire and forget”,异常会留在Task.Exception里,最终可能触发TaskScheduler.UnobservedTaskException - 如果要在非 await 场景下捕获,得显式
.Wait()或.Result(但会阻塞线程,不推荐) - ASP.NET Core 中未处理的异步异常常导致 500 且无日志,务必检查所有
await是否遗漏
真正难的不是语法,是判断该不该吞异常、在哪一层转化异常语义、以及 async 下异常是否真的落地了。这些地方一松懈,问题就藏进日志死角。







