catch 中的 when 是异常匹配阶段的前置条件,clr 先检查类型再求值表达式,为 false 则跳过整个 catch 块;它不是 if 语句,而是语言级语法,支持纯表达式但禁止抛异常或语句。

catch 里的 when 是什么,不是 if
它不是在 catch 块里加了个 if 判断,而是异常匹配阶段的前置条件。CLR 先检查类型是否匹配,再立刻求值 when 后的表达式;为 false 就跳过整个 catch 块,不进、不展开栈、不执行任何语句——连 Console.WriteLine 都不会跑。
常见错误是写成这样:
catch (Exception ex) {
if (ex.Message.Contains("超时")) {
// 处理逻辑
} else {
throw; // 错!这会破坏原始堆栈
}
}
换成 when 就干净了:
catch (InvalidOperationException ex) when (ex.Message.Contains("超时")) {
Log.Warn("超时异常,降级处理");
context.Result = new JsonResult(new { code = 504, msg = "请求超时" });
}
-
when表达式里能访问ex所有公开属性,也能读取局部变量、静态字段、配置项(比如Environment.IsDevelopment) - 别在
when里调用可能抛异常的方法(如JsonSerializer.Deserialize),否则会触发二次异常且难以调试 - 编译后,
when条件被内联到异常分发逻辑中,性能比catch + if + throw高
IExceptionFilter 和 when 过滤器根本不是一回事
别混淆:ASP.NET Core 的 IExceptionFilter 是 MVC 层的过滤器接口,作用域仅限于 Controller 的 Action 执行期间;而 catch ... when 是 C# 语言级语法,用在任意 try 块里,跟框架无关。
你写一个 try/catch 在 Service 方法里,就用 when;你在 Startup 中注册 AddExceptionFilter<myfilter>()</myfilter>,那是另一套机制。
-
IExceptionFilter.OnException必须设context.ExceptionHandled = true才生效,否则响应还是空或黄页 -
when不需要“标记已处理”,它只决定“进不进这个块”,进了就默认你全权负责 - 如果用了
[ApiController],模型验证失败(ModelState.IsValid == false)压根不走IExceptionFilter,但你自己的try/catch里仍可用when捕获手动抛的ValidationException
when 条件里能写什么,有哪些限制
when 后面只能是纯表达式,不能有语句。这意味着你不能在其中写 var x = 1;、foreach、using 或方法定义。但它支持三元运算、方法调用(只要不抛异常)、属性访问、LINQ 单行表达式。
典型安全写法:
catch (HttpRequestException ex)
when (ex.StatusCode == System.Net.HttpStatusCode.NotFound ||
ex.StatusCode == System.Net.HttpStatusCode.InternalServerError) {
// 处理特定 HTTP 状态码
}
- 允许:调用
ex.GetType().Name.Contains("Api")、读取Configuration["Feature:RetryEnabled"]、用DateTimeOffset.UtcNow.Hour > 22做时段控制 - 禁止:
File.ReadAllText("log.txt")(IO 可能失败)、throw new Exception()(语法错误)、lock(obj) { ... }(非表达式) - 调试时注意:
when表达式无法设断点,出问题得靠日志或提前把条件拆成变量观察
为什么有时候 when 没生效,却进了别的 catch
最常见原因是异常类型不匹配。比如你写了 catch (TimeoutException) when (...),但实际抛的是 TaskCanceledException(.NET 中两者不同),那这个 when 根本不会被评估。
另一个隐形坑:异步方法里没 await,导致异常发生在 Task 内部而非同步上下文,try/catch 根本包不住。
- 用
catch (Exception ex) when (...)能兜底,但别滥用——它会吃掉所有异常,掩盖类型意图 - 在 async 方法中,必须
await调用才能让异常进入当前try块;否则要用.GetAwaiter().GetResult()(不推荐)或捕获Task.Exception - 泛型约束类异常(如
ArgumentException<t></t>)需注意类型擦除,when里用ex is ArgumentException比依赖泛型名更稳
真正难调试的,往往是 when 条件里隐式依赖了未初始化的配置或空引用,结果条件恒为 false,你以为漏了异常,其实是它压根没进你写的那个 catch 块。










