0

0

c# 异常处理 try catch finally

月夜之吻

月夜之吻

发布时间:2025-12-30 03:31:25

|

580人浏览过

|

来源于php中文网

原创

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

c# 异常处理 try catch finally

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 块在绝大多数情况下都会运行,包括 returnbreakcontinue 甚至 throw 出当前方法时。但它不是绝对可靠的——比如进程被强制终止(Environment.FailFastThread.Abort 已废弃但仍有类似场景)、StackOverflow、OutOfMemory,或 Windows 上的硬关机。

所以 finally 适合做资源清理(如 stream.Close()conn.Dispose()),但不适合放关键业务逻辑(比如发通知、写审计日志)——这些应该放在 trycatch 中并单独容错。

  • using 替代手动 finally 关闭资源,更简洁且编译器保障
  • 如果必须手写 finally,优先调用 Dispose() 而非 Close()(后者可能不释放所有资源)
  • 不要在 finally 里写可能抛异常的代码,否则会覆盖原异常

多个 catch 块顺序错乱会导致子类异常永远捕不到

C# 的 catch 是从上到下匹配的,一旦某个 catch 满足类型条件就进入,不再检查后面的。所以如果把基类异常(如 Exception)写在子类(如 ArgumentNullException)前面,子类永远没机会触发。

try { ... }
catch (Exception ex) { /* 这里会捕获所有异常 */ }
catch (ArgumentNullException ex) { /* 永远不会进来 */ }

编译器其实会报错:CS0160 “先前已捕获到一个更加具体的异常类型”,但有些旧项目或动态编译场景可能绕过检查。

聚好用AI
聚好用AI

可免费AI绘图、AI音乐、AI视频创作,聚集全球顶级AI,一站式创意平台

下载
  • 总是按“从具体到宽泛”排列:先 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 下异常是否真的落地了。这些地方一松懈,问题就藏进日志死角。

相关专题

更多
java中break的作用
java中break的作用

本专题整合了java中break的用法教程,阅读专题下面的文章了解更多详细内容。

116

2025.10.15

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

253

2025.10.24

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

253

2025.10.24

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

364

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

559

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

364

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

559

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

466

2023.08.10

俄罗斯搜索引擎Yandex最新官方入口网址
俄罗斯搜索引擎Yandex最新官方入口网址

Yandex官方入口网址是https://yandex.com;用户可通过网页端直连或移动端浏览器直接访问,无需登录即可使用搜索、图片、新闻、地图等全部基础功能,并支持多语种检索与静态资源精准筛选。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1

2025.12.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.2万人学习

Excel 教程
Excel 教程

共162课时 | 9.9万人学习

PHP基础入门课程
PHP基础入门课程

共33课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号