用 catch + when 是最轻量、最安全、堆栈最完整的捕获方式;它在异常匹配阶段前置判断,条件不满足则完全不进入 catch 块,避免堆栈截断和性能损耗,而 catch 内 if + throw 会破坏原始堆栈且增加无谓开销。

直接说结论:用 catch + when 是最轻量、最安全、堆栈最完整的捕获方式;别在 catch 里判断再 throw,那会丢堆栈、伤性能。
为什么 when 比 catch 内部 if 更可靠
常见错误写法是:catch (Exception ex) { if (ex is InvalidOperationException && ex.Message.Contains("timeout")) { ... } else throw; }。这看似能筛选,但实际已触发异常捕获——堆栈被截断,调试时看不到原始抛出点,且每次都要进 catch 再判断,开销白费。
when 是编译器级筛选:条件不满足就完全不进入 catch 块,异常继续向上走,堆栈毫发无损。
- 必须用
when判断,不能只靠catch类型(比如catch (IOException)会捕获所有 IO 异常,太宽) -
when表达式里可访问ex全部属性,包括HResult、Source、自定义字段,甚至外部变量(如currentEnv == "prod") - 表达式必须是纯布尔计算,不能有副作用(比如不要在
when里写日志或调 API)
when 怎么匹配 COM 或平台特定错误码
Windows API、COM 组件、.NET 运行时底层异常常带精确 HResult,用 when 直接比对最稳。
例如权限拒绝错误:catch (COMException ex) when (ex.HResult == unchecked((int)0x80070005))
-
unchecked必须加,否则负数HResult编译报错 - 别用
ex.Message匹配——多语言系统下消息会变,HResult才是契约 - 若需多个错误码,用
||连接,如ex.HResult == 0x80070005 || ex.HResult == 0x80070006
如何避免 when 条件写错导致漏捕获
典型翻车场景:想捕获“数据库连接失败”,写了 when (ex.Message.Contains("connection")),结果生产环境日志里全是英文,而代码跑在中文系统上——Message 是本地化字符串,不可靠。
- 优先用异常类型 +
HResult/ErrorCode/ 自定义Code属性,而不是Message - 若必须用
Message,至少加StringComparison.OrdinalIgnoreCase,且确保测试覆盖不同语言环境 - 对
AggregateException,when只作用于外层异常,要检查内层得用ex.InnerException或ex.Flatten() - 注意空引用:
ex.InnerException?.Message在when里是允许的,但ex.InnerException.Message会 NRE 导致条件判 false(不是你想要的漏捕获,而是静默跳过)
和 ASP.NET Core 全局异常处理怎么共存
when 是语言层机制,只管 try-catch 范围内的异常;它和 IExceptionFilter、UseExceptionHandler 完全不冲突,但分工必须清楚。
-
when适合业务逻辑内部的精细分流(比如重试临时错误、跳过已知警告) -
IExceptionFilter适合 MVC Action 内统一记录 + 补充上下文,但记得设context.ExceptionHandled = true,否则没用 -
UseExceptionHandler是最后兜底,处理中间件、模型绑定等when根本触达不到的地方 - 别在
when里做耗时操作(如写 DB 日志),它在异常路径上,影响响应延迟
真正难的是分清异常发生的位置——模型绑定失败不会进 when,授权失败不会进 IExceptionFilter,400 错误压根不算异常。写过滤逻辑前,先确认它到底能不能被你选中的机制捕获到。


















