为什么向params方法传递null会导致一个空的参数数组?

34

我有一个使用 params 关键字的方法,就像这样:

private void ParamsMethod(params string[] args)
{
    // Etc...
}

然后,我使用不同的参数组合调用该方法:

                            // Within the method, args is...
ParamsMethod();             // - a string array with no elements
ParamsMethod(null);         // - null (Why is this?)
ParamsMethod((string)null); // - a string array with one element: null
ParamsMethod(null, null);   // - a string array with two elements: null and null
ParamsMethod("s1");         // - a string array with one element: "s1"
ParamsMethod("s1", "s2");   // - a string array with two elements: "s1" and "s2"

我理解所有的情况,除了第二种情况。有人能解释一下为什么ParamsMethod(null)导致args变成null,而不是一个含有一个空元素的数组吗?


1
既然你知道答案了,你可以解决这个难题了。你有两种方法:void M(object){}void M(params object[]){}。你调用 M(null)。哪个重载被选择,传递了什么参数以及为什么? - Eric Lippert
2
我以前遇到过这个问题。我想只是重命名了其中一个方法。 :) - FishBasketGordo
5个回答

30

params参数仅提供了一种方便的方式来指定值 - 您仍然可以直接传递数组引用。

现在,null 可以转换为 string[]string,因此两种解释都是有效的 - 这取决于规范中更喜欢哪个。 规范在第10.6.1.4节中指出:

  • 参数数组给定的参数可以是一个隐式转换为参数数组类型的单个表达式。 在这种情况下,参数数组的作用与值参数完全相同。

  • 或者,[...]

换句话说,编译器首先检查参数是否有效作为“正常”参数类型,只有在必须时才构建数组。


4
值得注意的是,许多接受参数数组的方法也会有重载的 (object)、(object, object) 和 (object, object, object) 版本 - 这是为了避免仅针对少量对象构造数组对象所产生的开销,因为这些重载版本将首先匹配。 - Mike Christensen

8
请参考C#规范第10.6.1.4节“参数数组”:
参数数组允许在方法调用中以两种方式指定参数: 1. 参数数组的给定参数可以是一个单一表达式,该表达式可以隐式转换(§6.1)为参数数组类型。在这种情况下,参数数组的行为与值参数完全相同。 2. 或者,调用可以为参数数组指定零个或多个参数,其中每个参数都是可以隐式转换(§6.1)为参数数组元素类型的表达式。在这种情况下,调用将创建一个参数数组类型的实例,其长度对应于参数的数量,并使用给定的参数值初始化数组实例的元素,并使用新创建的数组实例作为实际参数。
由于null可以隐式转换为string[],因此它将被用作数组。
此外:
当进行重载分辨率时,带有参数数组的方法可以在其正常形式或扩展形式(§7.5.3.1)中适用。仅当方法的正常形式不适用且与扩展形式具有相同签名的方法未在同一类型中声明时,才可使用该方法的扩展形式。
这说明编译器确实首先尝试使用带有数组参数的方法(正常形式),然后再尝试将参数用作数组元素(扩展形式)。

3
这是因为您可以将适当类型的数组传递给params参数。由于null可以转换为任何类型,并且数组语法具有优先级,因此您会得到null作为参数的值。
当然,将其明确转换为string会使其成为数组的元素而不是数组本身。
您可以尝试并查看:
private void DoSomething(params IEnumerable[] arr) {
    // ...
}

...

DoSomething(new IEnumerable[] {new int[] {}}); // arr[0] isn't IEnumerable[], it's int[].

And here's an online demo.


3
你可以将数组传递给params参数——事实上,这是更好的选择(例如,如果你将object[]传递给一个接受params object[]的方法,它将作为整个参数传递,而不仅仅是单个元素)。null也是数组的有效赋值,所以它会被绑定成功。
基本上,你需要更加明确。

2
从语言规范中可以看出:
参数数组允许在方法调用中以以下两种方式之一指定参数:
1. 给参数数组一个参数,该参数可以是一个隐式可转换为参数数组类型的单个表达式。在这种情况下,参数数组的行为与值参数完全相同。
2. 或者,调用可以为参数数组指定零个或多个参数,其中每个参数都是一个隐式可转换为参数数组元素类型的表达式。在这种情况下,调用将创建一个参数数组类型的实例,其长度对应于参数的数量,使用给定参数值初始化数组实例的元素,并将新创建的数组实例用作实际参数。
由于ParamsMethod(null)中的null可以隐式转换为(string [])null,因此适用第一条规则。

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