Exception 简介以及都有哪些类型【C# 基础】
〇、前言
先来看一个简单的场景:
凌晨两点,报警响了。
日志里只有一行:System.Exception: 未知错误。
你盯着这行字看了十分钟,什么也做不了。
它告诉你“出事了”,但没告诉你是谁出的事、能不能重试、要不要回滚事务。
这个 catch 写得越“保险”,你现在就越无助。
C# 里所有异常都继承自 Exception,但 Exception 本身几乎不携带任何决策信息。真正能定位问题、写出健壮代码的,是那几十个具体子类。
本文就来介绍下都有哪些常用的类别,后续再根据类别,通过示例来详细介绍。最终讲清楚它们各自代表什么、该什么时候抛、该什么时候捕。
一、Exception 简介
Exception 是 C# 唯一的错误通信机制。它可以让错误被正确分类、正确传递、正确处理;也可以在 catch 时能区分“我的错”“用户的错”“环境的错”“救不了的错”,而不是把所有失败当成同一种东西处理。
System.Object
└─ System.Exception ← 所有异常的基类(自定义异常也继承它)
├─ System.SystemException ← CLR 抛出的异常(文档已不推荐以此为基类)
└─ System.ApplicationException ← 早期留给用户异常的基类,现已废弃,别用它
注意:自定义异常请直接继承 Exception,不要再继承 ApplicationException。
Exception 的核心成员:
| 成员 | 说明 |
|---|---|
Message |
错误描述文本 |
InnerException |
引发当前异常的原始异常(包装异常时必设) |
StackTrace |
调用堆栈 |
HResult |
COM/Win32 风格的错误码 |
Data |
IDictionary,可附加自定义键值对 |
Source / TargetSite |
抛出源、出错方法 |
C# 里所有异常都继承自 System.Exception。日常开发中真正高频使用的只有二三十个,按“用途”分组记忆比死记硬背更有效。下面就先简单列一下全部分类。

二、都有哪些分类?
2.1 参数校验类(最常用,占日常 50%+)
| 异常类型 | 触发场景 | 典型写法 |
|---|---|---|
ArgumentNullException |
必填引用参数为 null | if (s == null) throw new ArgumentNullException(nameof(s)); |
ArgumentException |
参数值语义非法(非 null 但不合理) | throw new ArgumentException("路径不能包含通配符", nameof(path)); |
ArgumentOutOfRangeException |
数值超出允许范围 | throw new ArgumentOutOfRangeException(nameof(count), count, "必须大于 0"); |
DuplicateWaitObjectException |
等待对象数组中有重复项 | 较少手动抛 |
.NET 6+ 提供了更简洁的写法,推荐优先使用:
ArgumentNullException.ThrowIfNull(name); // .NET 6+
ArgumentException.ThrowIfNullOrEmpty(name); // .NET 7+
ArgumentException.ThrowIfNullOrWhiteSpace(name); // .NET 8+
注意:NullReferenceException 不应该由用户手动抛出。它是运行时在解引用 null 时自动抛出的;参数为 null 时应抛 ArgumentNullException,二者语义完全不同。
2.2 对象状态与操作合法性
| 异常类型 | 何时使用 |
|---|---|
InvalidOperationException |
对象当前状态不允许该操作。例如:未 Open 的 SqlConnection 执行查询、迭代器中集合被修改、枚举器已失效 |
ObjectDisposedException |
对象已被 Dispose 后仍被使用(Dispose 模式的标准选择) |
NotSupportedException |
接口成员在当前实现中不支持。例如:只读 Stream 的 Write、IList.Add 在固定大小列表上 |
NotImplementedException |
方法还没写完(临时占位),或明确留待子类实现 |
PlatformNotSupportedException |
当前 OS/运行时不支持该 API(如 Linux 上调用 Windows 注册表 API) |
TimeoutException |
操作超时(常用于异步、通信、锁等待) |
注意:NotSupportedException vs NotImplementedException:前者是“设计上就不支持”,后者是“还没做”。选错会让调用方误判。
2.3 集合与索引
IndexOutOfRangeException —— 数组/索引器越界
KeyNotFoundException —— Dictionary[key] 键不存在(建议先 TryGetValue)
ArrayTypeMismatchException —— 向数组写入不兼容类型(协变数组坑,如 object[] arr = new string[1]; arr[0] = 1;)
RankException —— 数组维度不匹配
InvalidOperationException —— 集合修改导致枚举失效(同上)
2.4 数值与类型转换
| 异常类型 | 场景 |
|---|---|
FormatException |
字符串格式不对,如 int.Parse("abc")、DateTime.Parse |
OverflowException |
数值溢出(仅在 checked 上下文或 Parse 严格模式下抛出) |
InvalidCastException |
显式转换失败,如 (int)"123"、as 后强转 |
ArithmeticException |
算术错误基类,一般用其子类 |
DivideByZeroException |
整数除以 0(浮点除 0 得 Infinity,不抛) |
NotFiniteNumberException |
出现 NaN/Infinity(极少见) |
实用建议:解析用户输入一律用 TryParse,避免靠 catch FormatException 做流程控制——性能差一个数量级。
2.5 文件与 IO(IOException 的子类)
IOException
├─ FileNotFoundException
├─ DirectoryNotFoundException
├─ PathTooLongException
├─ EndOfStreamException
├─ DriveNotFoundException
├─ FileLoadException ← 程序集加载失败(权限/平台问题)
└─ BadImageFormatException ← DLL/程序集格式损坏或位数不匹配(32 vs 64)
UnauthorizedAccessException—— 权限不足、文件只读、路径是目录等(不属于 IOException)DirectoryNotFoundException—— 路径部分不存在
try { File.ReadAllText(path); }
catch (FileNotFoundException) { /* 明确提示用户 */ }
catch (UnauthorizedAccessException) { /* 权限问题 */ }
catch (IOException ex) { /* 其他 IO 错误兜底 */ }
2.6 反射与程序集加载
TypeLoadException / ReflectionTypeLoadException —— 类型加载失败(后者含 LoaderExceptions 详细数组)。
TypeInitializationException —— 静态构造函数抛错(InnerException 才是真凶)。
MissingMethodException / MissingFieldException —— 版本不匹配,成员不存在。
MemberAccessException(基类)/ MethodAccessException / FieldAccessException / MissingMemberException。
TargetInvocationException —— 反射调用(MethodInfo.Invoke)时目标方法抛错,真实异常在 InnerException。
DllNotFoundException / EntryPointNotFoundException —— P/Invoke 找不到 native DLL 或入口函数。
2.7 并发、任务与异步
| 异常类型 | 说明 |
|---|---|
AggregateException |
Task / PLINQ 把多个异常打包。用 Flatten().InnerExceptions 遍历 |
TaskCanceledException |
Task 被取消(继承自 OperationCanceledException) |
OperationCanceledException |
CancellationToken 触发取消 |
ThreadInterruptedException |
线程被中断 |
ThreadAbortException |
Thread.Abort(已过时,.NET Core 上不再支持) |
TimeoutException |
异步超时 |
2.8 内存与线程严重异常
OutOfMemoryException —— 内存不足。注意:大对象分配失败、甚至某些集合扩容失败都可能抛它;捕获后通常无法恢复。
StackOverflowException —— 栈溢出(无限递归)。.NET 2.0 起无法被 catch,进程直接终止。
InsufficientMemoryException / MemoryFailPoint —— 内存检查点。
AppDomainUnloadedException —— 访问已卸载的 AppDomain。
ThreadAbortException —— 线程中止。
2.9 互操作与系统层
COMException —— COM 调用失败,ErrorCode 为 HRESULT。
SEHException —— 非托管结构化异常。
Win32Exception —— Win32 API 错误(NativeErrorCode + Message 自动取系统描述)。
SecurityException / VerificationException —— 代码访问安全(现代 .NET 中基本消失)。
SerializationException —— 序列化失败。
HttpRequestException(System.Net.Http)—— HTTP 请求失败。
DbException / SqlException(ADO.NET)—— 数据库错误,含 Number、State、ErrorCode。
三、常见的抛异常的场景对应的 Exception 类型
| 想表达的意思 | 应该抛 |
|---|---|
| 参数是 null | ArgumentNullException |
| 参数值不合理 | ArgumentException / ArgumentOutOfRangeException |
| 对象状态不对(顺序错了、没初始化) | InvalidOperationException |
| 已经 Dispose 了 | ObjectDisposedException |
| 这个实现天生不支持 | NotSupportedException |
| 还没写 | NotImplementedException |
| 平台不支持 | PlatformNotSupportedException |
| 超时 | TimeoutException |
| 找不到文件/配置 | FileNotFoundException / KeyNotFoundException |
| 外部依赖失败(网络、DB) | HttpRequestException / DbException,或包一层自定义异常 |
| 业务规则违反 | 自定义异常(如 OrderCannotBeShippedException) |
本文就介绍到这里,后续再针对各个类型进行详细介绍和示例。
本文来自博客园,作者:橙子家,欢迎微信扫码关注博主【橙子家czzj】,有任何疑问欢迎沟通,共同成长!

浙公网安备 33010602011771号