在编写一些特别复杂的异常处理代码时,有人问,你不需要确保你的异常对象不为空吗?我说,当然不是,但后来决定试一试。显然,你可以抛出 null,但它仍然在某处变成了异常。
为什么允许这样做?
throw null;
在此代码段中,值得庆幸的是“ex”不为空,但它有可能是空的吗?
try
{
throw null;
}
catch (Exception ex)
{
//can ex ever be null?
//thankfully, it isn't null, but is
//ex is System.NullReferenceException
}
最佳答案
因为语言规范需要类型为 System.Exception
的表达式(因此,null
在该上下文中是有效的)并且不限制此表达式为非空。通常,它无法检测该表达式的值是否为 null
。它必须解决停机问题。无论如何,运行时都必须处理 null
的情况。见:
Exception ex = null;
if (conditionThatDependsOnSomeInput)
ex = new Exception();
throw ex;
当然,他们可以使抛出 null
文字的特定情况无效,但这不会有太大帮助,那么为什么要浪费规范空间并降低一致性而得不到什么好处呢?
免责声明(在我被 Eric Lippert 扇耳光之前):这是我自己对该设计决策背后原因的推测。当然,我没有参加过设计 session ;)
第二个问题的答案,捕获在 catch 子句中的表达式变量是否可以为 null:虽然 C# 规范没有说明其他语言是否会导致传播 null
异常,它确实定义了传播异常的方式:
The catch clauses, if any, are examined in order of appearance to locate a suitable handler for the exception. The first catch clause that specifies the exception type or a base type of the exception type is considered a match. A general catch clause is considered a match for any exception type. [...]
对于null
,粗体语句为false。因此,虽然完全基于 C# 规范所说的内容,但我们不能说底层运行时永远不会抛出 null,我们可以确定即使是这种情况,它也只会由通用 处理捕获 {}
子句。
对于 CLI 上的 C# 实现,我们可以引用 ECMA 335 规范。该文档定义了 CLI 内部抛出的所有异常(没有一个是 null
),并提到用户定义的异常对象是由 throw
指令抛出的。该指令的描述实际上与 C# throw
语句相同(除了它不将对象类型限制为 System.Exception
):
Description:
The
throw
instruction throws the exception object (typeO
) on the stack and empties the stack. For details of the exception mechanism, see Partition I.
[Note: While the CLI permits any object to be thrown, the CLS describes a specific exception class that shall be used for language interoperability. end note]Exceptions:
System.NullReferenceException
is thrown ifobj
isnull
.Correctness:
Correct CIL ensures that object is always either
null
or an object reference (i.e., of typeO
).
我相信这些足以得出捕获的异常永远不会 null
的结论。
关于c# - 为什么 C# 允许您使用 'throw null' ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2195764/