当传递“null”常量时为什么会出现异常,但传递“null”字符串引用时却不会出现异常?

59

如果我运行这段代码:

Console.WriteLine( String.Format( "{0}", null ) );

当我运行这段代码时,我会得到一个ArgumentNullException异常:

String str = null;
Console.WriteLine( String.Format( "{0}", str ) );

它完全正常运行,输出为空字符串。

现在这两个部分对我来说看起来是等价的 - 它们都将一个 null 引用传递给 String.Format(),但行为不同。

这里如何可能有不同的行为?


3
Console.WriteLine(String.Format("{0}", (object)null)); 这也可以。奇怪。 - Dave Bish
String.Format 可能对第二个示例进行了一些处理,也许将其转换为 null 字符 \0。 </猜测> - SpaceBison
@DaveBish 希望 Jon Skeet 能够解释为什么你的示例没有抛出异常。 - juharr
第一种情况是传递一个空引用。第二种情况是传递一个指向空字符串的(非空)引用。这是微妙但重要的区别。编辑:我对C#不是很熟悉,但我假设“String str = null;”是将一个值赋给已声明的对象。如果不是这样的话,那么我可能是错误的。 - CuriousRabbit
1个回答

76

只需反编译代码即可了解其运作方式。

string.Format("{0}", null)

调用最具体的适用重载,即string.Format(string, object[])

string.Format 的重载有:

Format(String, Object)
Format(String, Object[])
Format(IFormatProvider, String, Object[])
Format(String, Object, Object)
Format(String, Object, Object, Object)

希望最后三个选项为什么无效是显而易见的。

为了确定使用前两者中的哪一个,编译器会将从nullObject的转换与从nullObject[]的转换进行比较。将null转换为Object[]被认为是“更好”的,因为存在从Object[]Object的转换,但反之不成立。这与我们如果有:

Foo(String)
Foo(Object)

并调用 Foo(null),它将选择 Foo(String)

因此,您的原始代码等效于:

object[] values = null;
string.Format("{0}", values);

现在,希望您如文档所述,会得到一个ArgumentNullException异常。


1
我打赌你在最后一行代码中想表达的是 string.Format("{0}", values);。无论如何,由于你比我打得更快、更准确,这给你加了一个赞 ^^' - Edurne Pascual
3
"take my money" 按钮在哪里? - Soner Gönül
另外,也许答案应该强调为什么编译器选择函数的最具体版本(对于我来说很容易理解,我知道你也很容易理解,但许多读者可能会忽略null字面量所携带的类型信息微不足道,就像是一张空白纸)。 - Edurne Pascual
为什么string.Format("{0}", (object)null)不会抛出异常?而且文档中只提到当格式为null时才会抛出异常,没有提及参数的情况。 - juharr
1
@juharr,对象数组中的一个(或多个,或全部)成员为空是可以的。但是如果数组本身为空,则不行。string.Format(string, object[])版本默认将空数组转换为空数组会更好,但它并没有这样做。 - Edurne Pascual
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接