在编写一些特别复杂的异常处理代码时,有人问,你不需要确保你的异常对象不是null吗?我说,当然不是,但后来决定尝试一下。显然,你可以抛出null,但它仍然会在某处变成异常。
为什么允许这样做?
throw null;
在这个片段中,谢天谢地'ex'不是null,但它可能永远是吗?
try
{
throw null;
}
catch (Exception ex)
{
//can ex ever be null?
//thankfully, it isn't null, but is
//ex is System.NullReferenceException
}
答案 0 :(得分:82)
因为语言规范需要类型为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#规范所说的内容,但我们不能说底层运行时不会抛出null,我们可以肯定即使是这种情况,它也只能由泛型{{1子句。
对于CLI上的C#实现,我们可以参考ECMA 335规范。该文档定义了CLI在内部抛出的所有异常(均不是catch {}
),并提到null
指令抛出用户定义的异常对象。该指令的描述与C#throw
语句几乎完全相同(除了它不会将对象的类型限制为throw
):
说明
System.Exception
指令在堆栈上抛出异常对象(类型throw
)并清空堆栈。有关异常机制的详细信息,请参阅分区I.
[注意:虽然CLI允许抛出任何对象,但CLS描述了一个特定的异常类,它将用于语言互操作性。结束说明]例外:
如果O
为System.NullReferenceException
,则会引发{p>obj
。正确性:
正确CIL确保对象始终为
null
或对象引用(即类型为null
)。
我相信这些足以导致被捕获的异常永远不会O
。
答案 1 :(得分:25)
显然,你可以抛出null,但它仍然会在某处变成异常。
尝试抛出null
对象会导致(完全不相关)空引用异常。
问你为什么被允许抛出null
就像问你为什么被允许这样做:
object o = null;
o.ToString();
答案 2 :(得分:5)
虽然可能无法在C#中抛出null,因为throw会检测到它并将其转换为NullReferenceException,但是可能会收到null ...我碰巧现在正在接收它,这会导致我的捕获(不期望'ex'为null)经历一个空引用异常,然后导致我的应用程序死亡(因为那是最后一次捕获)。
所以,虽然我们不能从C#中抛出null,但是netherworld可以抛出null,所以你最外层的catch(Exception ex)更好地准备接收它。仅供参考。
答案 3 :(得分:4)
取自here:
如果在C#中使用此表达式 代码它将抛出一个 NullReferenceException异常。那是 因为throw语句需要一个 Exception类型的对象 参数。但这个目标是 在我的例子中为null。
答案 4 :(得分:2)
我想也许你不能 - 当你尝试抛出null时,它不能,所以它在错误的情况下做它应该做的,这是抛出一个空引用异常。所以你实际上并没有抛出null,你没有抛出null,导致抛出。
答案 5 :(得分:2)
试图回答“..谢谢'ex'不是空的,但它可能永远是吗?”:
由于我们可以说不能抛出null的异常,因此catch子句也永远不会捕获null的异常。因此,ex永远不会为空。
我现在看到这个问题实际上已经是asked。
答案 6 :(得分:1)
请记住,异常包括抛出异常的详细信息。看到构造函数不知道它将被抛出的位置,那么只有throw方法将这些细节注入到throw中的对象才有意义。换句话说,CLR正在尝试将数据注入null,这会触发NullReferenceException。
不确定这是否正是发生的事情,但它解释了这种现象。
假设这是真的(我不能认为更好的方法是将ex变为null而不是抛出null;),这意味着ex不可能为null。
答案 7 :(得分:0)
在较旧的c#中:
考虑以下语法:
public void Add<T> ( T item ) => throw (hashSet.Add ( item ) ? null : new Exception ( "The item already exists" ));
我认为它比这还短:
public void Add<T> ( T item )
{
if (!hashSet.Add ( item ))
throw new Exception ( "The item already exists" );
}