为啥将 null 传递给 params 方法会导致 null 参数数组?

Posted

技术标签:

【中文标题】为啥将 null 传递给 params 方法会导致 null 参数数组?【英文标题】:Why does passing null to a params method result in a null parameter array?为什么将 null 传递给 params 方法会导致 null 参数数组? 【发布时间】:2012-02-20 00:03:22 【问题描述】:

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

【问题讨论】:

既然您知道了答案,您就可以解决这个难题了。您有两种方法,void M(object)void M(params object[])。你打电话给M(null)。选择了哪个重载,传递给它的是什么,为什么? 我以前遇到过这种情况。我认为只是重命名了其中一种方法。 :) 【参考方案1】:

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

现在,null 可以转换为 string[]string,因此两种解释都有效 - 取决于首选规范。该规范在第 10.6.1.4 节中指出:

为参数数组提供的参数可以是单个表达式,可隐式转换为参数数组类型。在这种情况下,参数数组的作用就像值参数一样。

或者,[...]

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

【讨论】:

另外,值得注意的是,许多采用参数数组的方法也会对 (object)、(object, object) 和 (object, object, object) 进行重载 - 这是为了避免只为少数对象构造数组对象的开销,因为这些重载将首先匹配。【参考方案2】:

参见 C# 规范,10.6.1.4 参数数组部分

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

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

由于null 可以隐式转换为string[],它将被用作数组。

此外:

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

这阐明了编译器确实首先尝试将方法与数组argument(普通形式)一起使用,然后再尝试将参数用作数组element(扩展形式) )。

【讨论】:

【参考方案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.

【讨论】:

【参考方案4】:

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

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

【讨论】:

【参考方案5】:

来自语言规范:

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

为参数数组提供的参数可以是单个表达式,可隐式转换(第 6.1 节)为参数数组类型。在这种情况下,参数数组的作用就像值参数一样。

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

由于ParamsMethod(null) 中的null 可以隐式转换为(string[])null,所以第一个规则就是被应用的规则。

【讨论】:

以上是关于为啥将 null 传递给 params 方法会导致 null 参数数组?的主要内容,如果未能解决你的问题,请参考以下文章

为啥将“switch”类型参数的值传递给字符串参数?

为啥将 lambda 传递给受约束的类型模板参数会导致“不完整类型”编译器错误?

为啥从 jquery 传递给控制器​​方法的参数即使不是 null 也是 null?

为啥 registerForContextMenu 将 onCreateContextMenu 传递给 null ContextMenuInfo?

为啥不可能将互斥锁传递给线程?

将带有参数的 c# 回调方法传递给 c++ dll 会导致 System.ExecutionEngineException