跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

C#怎么实现自定义异常类 C#如何继承Exception定义业务异常和统一异常处理机制【语法】

自定义异常类必须继承Exception,覆盖三个构造函数并调用base(...);命名需带Exception后缀且体现领域意图;统一处理应使用UseExceptionHandler中间件而非基类try/catch;二次抛出须用throw而非throw ex以保留原始堆栈。 自定义异常类必须继承
Exception
或其子类 直接继承
Exception
是最稳妥的做法,别图省事去继承
SystemException
或其他中间类——它们有框架语义,乱用会导致异常分类混乱。构造函数至少得覆盖三个常用重载:
Exception()
、
Exception(string message)
、
Exception(string message, Exception innerException)
。缺了带
innerException
的那个,链式异常信息就断了,线上查问题时会丢关键上下文。 常见错误现象:抛出后用
ex.ToString()
看不到原始异常堆栈;或全局异常捕获里
ex.InnerException
为
null
,但实际业务逻辑里明明传了。 务必在每个构造函数里调用
base(...)
,别漏掉 不要在异常类里加 public 属性或业务字段——序列化和跨 AppDomain 时可能失败 如果真需要附带业务码,用只读属性 +
protected set
,并在构造中赋值 业务异常命名要带
Exception
后缀且体现领域意图 比如
InsufficientBalanceException
比
BalanceException
或
BalanceError
更准确;
OrderNotFoundException
明确表达“查不到”,而不是模糊的
OrderInvalidException
(后者更适合校验失败场景)。.NET 生态默认约定所有异常类型名以
Exception
结尾,不遵守会影响工具识别(如 IDE 快速修复、静态分析规则)。 使用场景:API 接口层做参数校验失败时 throw
InvalidParameterException
,仓储层查不到记录时 throw
ProductNotFoundException
,这两者不该混用——统一异常处理中间件靠类型区分响应状态码。 避免泛化命名:如
BusinessException
、
AppException
,等于没分 不要用动词开头:如
ThrowOrderException
,这是方法名习惯 英文名保持 PascalCase,别写成
insufficient_balance_exception
统一异常处理不能只依赖
try/catch
全局包裹 ASP.NET Core 里真正可靠的是
UseExceptionHandler
中间件 + 自定义
ExceptionHandler
,而不是在 Controller 基类里写一个万能
try/catch
。前者能捕获未处理异常、支持异步上下文、可注入服务;后者容易漏掉过滤器、模型绑定、中间件异常,且无法处理
Task
内部未 await 的异常。 C知道 CSDN推出的一款AI技术问答工具 下载 性能影响:中间件方式只在真正出错时执行,而基类
try/catch
是每次请求都进一层 try 块,CLR 异常机制本身无开销,但滥用 catch 会干扰 JIT 优化路径。
UseExceptionHandler
配置后,确保
app.UseRouting()
在它之前,否则路由异常进不来 在 handler 里别直接
throw ex
,要用
context.Response.StatusCode = 500
显式设状态码 若需记录日志,从
context.Features.Get()
拿原始路径,别依赖
HttpContext.Request.Path
throw
和
throw ex
的区别直接影响异常链完整性 在 catch 块里二次抛出时,用
throw
(无参数)保留原始堆栈;用
throw ex
会重置堆栈起点到当前行,导致上层看到的异常位置是“重新抛出处”而非“最初发生处”。这是线上排障最常踩的坑——日志里显示异常在
GlobalExceptionHandler.Invoke
里,其实根源在 Repository 的第 3 行 SQL 执行。 兼容性注意:C# 6+ 支持异常过滤器(
catch (MyException ex) when (ex.Code == 1001)
),此时仍该用
throw
继续向上抛,别误写成
throw ex
。 想补充信息?用
throw new MyException("额外说明", ex)
,把原异常当
innerException
不要为了“加日志”而在 catch 里
throw ex
,日志写完直接
throw
异步方法里
await
后的
throw
依然保持原始堆栈,放心用 异常类型的粒度和
throw
时的堆栈控制,比怎么写 handler 逻辑更关键。很多人花大力气写统一返回格式,却在第一处
throw ex
就把根因藏没了。

相关文章