C# 为什么将null传递给params方法会导致null参数数组?

C# 为什么将null传递给params方法会导致null参数数组?,c#,.net,.net-3.5,params,C#,.net,.net 3.5,Params,我有一个使用params关键字的方法,如下所示: private void ParamsMethod(params string[] args) { // Etc... } 然后,我使用各种参数组合调用该方法: // Within the method, args is... ParamsMethod(); // - a string array with no elements ParamsMethod(

我有一个使用
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
,而不是一个包含一个null元素的数组吗?

一个
params
参数只是为了提供一种方便的方法来指定值-您仍然可以直接传递数组引用

现在,
null
可以转换为
string[]
string
,因此这两种解释都是有效的-取决于首选的规范。本规范第10.6.1.4节规定:

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

  • 或者,[……]


换句话说,编译器首先检查参数是否作为“正常”参数类型有效,并且仅在绝对必要时构建数组。

原因是您可以将适当类型的数组传递给
参数。由于
null
可以转换为任何类型,并且数组语法优先,因此可以将
null
作为参数值

当然,显式地将其强制转换为
string
,会使其成为数组的一个元素,而不是数组本身

您可以试试看:

private void DoSomething(params IEnumerable[] arr) {
    // ...
}

...

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

您可以将数组传递给params参数-事实上,这是更好的选择(即,如果您将对象[]传递给采用params对象[]的方法,则它是整个参数,而不仅仅是单个元素)。Null作为数组的赋值是有效的,因此-绑定获胜

基本上,您需要更加明确。

请参阅C#规范,第10.6.1.4节参数数组:

参数数组允许在方法调用中以以下两种方式之一指定参数:

  • 参数数组的参数可以是一个表达式,该表达式可以隐式转换为参数数组类型(§6.1)。在这种情况下,参数数组的作用与值参数完全相同
  • 或者,调用可以为参数数组指定零个或多个参数,其中每个参数都是一个表达式,可以隐式转换为参数数组的元素类型(§6.1)。在这种情况下,调用将创建一个长度与参数数量对应的参数数组类型实例,使用给定的参数值初始化数组实例的元素,并使用新创建的数组实例作为实际参数
由于
null
可隐式转换为
string[]
,因此它将用作数组

此外:

当执行重载解析时,带有参数数组的方法可能以其标准形式或扩展形式适用(§7.5.3.1)。仅当方法的标准形式不适用时,并且仅当与扩展形式具有相同签名的方法尚未在同一类型中声明时,方法的扩展形式才可用


这澄清了编译器在尝试将参数用作数组元素(扩展形式)之前,确实首先尝试将该方法与数组参数(标准形式)一起使用。

来自语言规范:

参数数组允许在方法调用中以以下两种方式之一指定参数:

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

  • 或者,调用可以为参数数组指定零个或多个参数,其中每个参数都是一个表达式,可以隐式转换为参数数组的元素类型(§6.1)。在这种情况下,调用将创建一个长度与参数数量对应的参数数组类型实例,使用给定的参数值初始化数组实例的元素,并使用新创建的数组实例作为实际参数


由于
ParamsMethod(null)
中的
null
可以隐式转换为
(string[])null
,因此第一条规则是应用的规则。

此外,值得注意的是,许多采用参数数组的方法也会对(object)、(object、object)和(object、object、object)进行重载-这是为了避免仅为少数对象构造数组对象的开销,因为这些重载将首先匹配。现在您知道了答案,可以处理此难题。有两种方法,
void M(object){}
void M(params object[]){}
。您可以调用
M(null)
。选择哪个重载,传递给它什么,为什么?我以前遇到过这种情况。我想刚刚重命名了其中一种方法。:)