c# 异常处理 try catch finally

来源:这里教程网 时间:2026-02-21 17:36:05 作者:

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 下异常是否真的落地了。这些地方一松懈,问题就藏进日志死角。

相关推荐