因为语言规范期望在System.Exception
那里有一个类型的表达式(因此,null
在该上下文中是有效的),并且不将该表达式限制为非null。通常,无法检测到该表达式的值是否正确null
。它必须解决停止问题。null
无论如何,运行时都必须处理这种情况。看到:
Exception ex = null;
if (conditionThatDependsOnSomeInput)
ex = new Exception();
throw ex;
当然,他们可以使抛出null
字面量的特定情况无效,但这无济于事,那么为什么要浪费规格空间并降低一致性却没有什么好处?
免责声明(在我被Eric Lippert打之前):这是我自己对此设计决定背后的原因的推测。当然,我还没有参加设计会议;)
第二个问题的答案是,在catch子句中捕获的表达式变量是否可以为null:尽管C#规范对其他语言是否会导致null
异常的传播保持沉默,但它确实定义了异常的传播方式:
如果有catch子句,则按照出现的顺序对其进行检查,以找到合适的异常处理程序。指定异常类型或异常类型的基本类型的第一个catch子句被视为匹配项。常规catch子句被认为是任何异常类型的匹配项。[...]
对于null
,粗体语句为false。因此,尽管纯粹基于C#规范所说的,我们不能说底层运行时永远不会抛出空值,但是我们可以确保即使是这种情况,也只能由通用catch {}
子句处理。
对于CLI上的C#实现,我们可以参考ECMA 335规范。该文档定义了CLI内部抛出的所有异常(都不是null
),并提到了该throw
指令抛出了用户定义的异常对象。该指令的描述实际上与C#throw
语句相同(除了它不将对象的类型限制为System.Exception
):
描述:
该throw
指令将异常对象(类型O
)抛出到堆栈上并清空堆栈。有关异常机制的详细信息,请参见分区I。
[注:尽管CLI允许抛出任何对象,但CLS描述了特定的异常类,该类将用于语言互操作性。尾注]
例外情况:
System.NullReferenceException
如果obj
是则抛出null
。
正确性:
正确的CIL确保对象始终是null
或对象引用(即类型O
)。
我相信这些足以推断出从未发生过的例外情况null
。