string.Format如何处理空值?

71
在下面的代码中,为什么两个string.Format调用的行为不同?在第一个调用中,没有抛出异常,但在第二个调用中抛出了ArgumentNullException
static void Main(string[] args)
{
    Exception e = null;
    string msgOne = string.Format("An exception occurred: {0}", e);
    string msgTwo = string.Format("Another exception occurred: {0}", null);
}

可以有人帮我理解这两者之间的区别吗?


7
很可能你在第二个示例中触发了params覆盖,而且String.Format在继续迭代集合并插入值之前会测试数组是否被填充。 - Brad Christie
@BradChristie 你应该把那写成一个答案。 - erikkallen
5个回答

57

我猜测这里的问题在于你调用了不同类型的重载函数;String.Format有多个版本。

第一个例子中,你调用的很可能是String.Format(string,object)

第二个例子中,由于提供了null,你很可能调用了String.Format(string,params object[]),根据文档说明,当以下情况之一发生时,它会引发ArgumentNullException

format 或 args 为 null。

如果你正在运行.NET4,则可以尝试使用命名参数:

String.Format("Another exception occured: {0}", arg0: null);

为什么会调用 params object[] 这个重载?可能是因为 null 不是一个对象,而 params 的工作方式是可以在调用时将每个值作为新对象传递,也可以将其作为值的数组传递。也就是说,以下两种方式等价

String.Format("Hello, {0}! Today is {1}.", "World", "Sunny");
String.Format("Hello, {0}! Today is {1}.", new Object[]{ "World", "Sunny" })

因此,它将把您的语句调用翻译成类似以下的内容:

String format = "Another exception occured: {0}";
Object[] args = null;
String.Format(format, args); // throw new ArgumentNullException();

1
还有一个可能性:string.Format("Occurred: {0}", new[] { (object)null, })。我猜问题只会发生在第一种情况下。此外,请尝试区分 string.Format("Occurred: {0}", (object[])null)string.Format("Occurred: {0}", (object)null) 的差异。 - Jeppe Stig Nielsen

12

在您的第一个示例中,您使用了Format(String, Object)方法,反汇编后代码如下:

 public static string Format(string format, object arg0)
 {
    return Format(null, format, new object[] { arg0 });
 }

注意周围的new object[]

第二个例子,显然你正在使用Format(string, object[]),至少当我执行相同的测试时是这样的。反汇编后,它看起来像这样:

 public static string Format(string format, params object[] args)
 {
     return Format(null, format, args);
 }

所以所有这些实际上都被汇集到Format(IFormatProvider, string, object[])。 酷,让我们看一下那里的前几行:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
...
}

哎呀,问题就在这里!第一个调用将其包装在一个新的数组中,因此它不是null。明确传入null并不会使其这样做,这是由调用Format()的特定实例决定的。


8
如果您使用插值字符串($"",另一种格式化方式),则会忽略null并跳过它。因此,
string nullString = null;
Console.WriteLine($"This is a '{nullString}' in a string");

将会产生:"这是一个''在字符串中"。当然,如果为 null 可以使用空合并运算符来产生所需的输出:

string nullString = null;
Console.WriteLine($"This is a '{nullString ?? "nothing"}' in a string");

2
第一个调用被解析为对Format(object)的调用,而第二个调用被解析为对Format(object[])的调用。这些不同的重载函数处理空参数的方式不同。
重载决议在这里中有描述。相关部分是,对于第二次对Format的调用,Format(params object[])的重载被扩展为Format(object[]),这比Format(object)更优先。字面上的null既是object[]又是object,但是object[]更具体,因此被选择。

-4

以下是两个不同之处:

  1. 这里将Null值赋给了变量。

    Exception e = null;
    string msgOne = string.Format("发生异常:{0}", e);
    
  2. 这里无法以字符串格式读取Null值,这意味着类型转换错误。

    string msgTwo = string.Format("另一个异常发生:{0}", null);
    
我给你一个简单的例子: 在这里,您不能将NULL值读取为字符串格式。
string str = null.toString();

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