因为语言规范在那里期望一个类型为 System.Exception
的表达式(因此,在该上下文中,null
是有效的),并且不限制该表达式必须非空。一般来说,它无法检测该表达式的值是否为 null
。它将不得不解决停机问题。运行时仍然需要处理 null
情况。请参见:
Exception ex = null;
if (conditionThatDependsOnSomeInput)
ex = new Exception();
throw ex;
他们当然可以使抛出 null 字面量的特定情况无效,但这并没有什么帮助,为什么浪费规范空间并减少一致性来换取少量的利益呢?
免责声明(在 Eric Lippert 拍我之前):这是我自己关于这个设计决策背后推理的猜测。当然,我没有参加过这个设计会议 ;)
你第二个问题的答案是,一个在 catch 子句中捕获的表达式变量是否可能为空值:虽然 C# 规范对其他语言是否会引起 null 异常传播保持沉默,但它确实定义了异常传播的方式:
如果有 catch 子句,则按照出现的顺序依次检查以找到适合的异常处理程序。第一个指定异常类型或异常类型基类的 catch 子句被视为匹配项。对于任何异常类型,通用 catch 子句都被视为匹配项。...
对于 null,粗体陈述是错误的。因此,尽管根据 C# 规范纯粹地说,我们不能说底层运行时不会抛出 null,但我们可以确定即使是这种情况,也只能由通用的 catch {} 子句处理。
对于 CLI 上的 C# 实现,我们可以参考 ECMA 335 规范。该文档定义了 CLI 内部抛出的所有异常(其中没有一个是 null),并提到用户定义的异常对象由 throw 指令抛出。该指令的描述与 C# 的 throw 语句几乎完全相同(除了它不限制对象的类型为 System.Exception):
throw 指令在堆栈上抛出异常对象(类型为 O),并清空堆栈。有关异常机制的详细信息,请参见 Partition I。
[注意:虽然 CLI 允许抛出任何对象,但 CLS 描述了应用于语言互操作性的特定异常类。结束说明]
如果 obj 为 null,则引发 System.NullReferenceException。
正确定 CIL 确保对象始终为 null 或对象引用(即类型为 O)。
我认为这些足以得出结论,捕获的异常永远不会是 null。
throw
这个本来就是为了抛出异常而存在的语句来说,警告并没有太多价值。 - Mehrdad Afshari