在 C# 编程中,`throw` 关键字用于显式地引发异常。它不仅用于报告错误,还在控制程序流程和异常传播中扮演关键角色。
最基本的形式是抛出一个继承自 `System.Exception` 的对象实例。
public void ValidateUser(string name) { if (name == null) { // 基础用法:实例化并抛出 throw new ArgumentNullException(nameof(name), "用户名不能为空"); } }
注意:一旦 `throw` 语句执行,当前方法的后续代码将不再运行,控制权将转交给调用堆栈中最近的 `catch` 块。
这是 C# 开发中最容易出错的地方。当你在 `catch` 块中捕获异常并希望将其传递给上层调用者时,有两种写法,但结果截然不同。
使用不带参数的 `throw;` 可以保留原始异常的堆栈跟踪(Stack Trace)。
try { ProcessData(); } catch (Exception ex) { Log(ex); // ✅ 正确:保留原始堆栈信息,就像错误是在 ProcessData 内部发生的一样 throw; }
如果你抛出了捕获的异常变量(`throw ex;`),堆栈跟踪会被重置。
try { ProcessData(); } catch (Exception ex) { Log(ex); // ❌ 错误:堆栈跟踪被重置,看起来错误好像是发生在这行代码,而不是 ProcessData 内部 throw ex; }
从 C# 7.0 开始,`throw` 不仅仅是一个语句,还可以作为一个表达式使用。这意味着你可以在赋值、条件运算符(三元运算符)或 Lambda 表达式中直接使用它。
这是最常见的用法,用于简化参数验证。
public class Person { public string Name { get; } public Person(string name) { // 如果 name 为 null,直接抛出异常,否则赋值 Name = name ?? throw new ArgumentNullException(nameof(name)); } }
string GetGrade(int score) { return (score >= 0 && score <= 100) ? (score >= 60 ? "Pass" : "Fail") : throw new ArgumentOutOfRangeException(nameof(score), "分数必须在0到100之间"); }
Func<string, string> cleaner = s => s ?? throw new Exception("字符串不能为null");
在 C# 中使用 `throw` 时,应尽量抛出 .NET 框架提供的标准异常,而不是通用的 `Exception`。
| 异常类型 | 适用场景 |
|---|---|
| ArgumentNullException | 参数值为 null,但该方法不允许 null。 |
| ArgumentOutOfRangeException | 参数值超出了允许的范围(例如索引越界)。 |
| ArgumentException | 参数无效,但不属于上述两种情况。 |
| InvalidOperationException | 当对象处于不适合执行该方法的状态时(例如在连接关闭时尝试读取数据库)。 |
| NotImplementedException | 方法尚未实现(通常用于开发阶段)。 |