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。日常开发中真正高频使用的只有二三十个,按“用途”分组记忆比死记硬背更有效。下面就先简单列一下全部分类。

image

二、都有哪些分类?

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)

本文就介绍到这里,后续再针对各个类型进行详细介绍和示例。

posted @ 2026-10-15 17:54  橙子家  阅读(18)  评论(0)    收藏  举报