C# params object[] 奇怪的行为

C# params object[] strange behavior

考虑这段代码

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] strings = new string[] { "Test1", "Test2", "Test3" };

            int[] ints = new int[] { 1, 2, 3, 4 };

            Test(strings);

            Test(ints);
        }

        public static void Test(params object[] objects)
        {
        }
    }
}

还有这个页面https://msdn.microsoft.com/fr-ca/library/w5zay9db.aspx

我希望 (params object[] objects) 是一个元素的数组,第一个元素是 string[],但是当我调试时,我看到 (params object[] objects) 是 { "Test1", "Test2", "Test3" }.

但是,使用 int[],我得到一个对象[],其中 int[] 作为第一个元素。

这是未定义的行为吗?这是否取决于 .Net 框架版本/Mono 版本?

我不是专家,但 params 关键字的想法是允许对方法进行不同的调用,而不管您有多少元素。

Test(object1)
Test(object1, object2)
Test(object1,..., objectN)

所以您看到的是正常行为,没有什么奇怪的。关于此的更多信息 link msdn

By using the params keyword, you can specify a method parameter that takes a variable number of arguments.

You can send a comma-separated list of arguments of the type specified in the parameter declaration or an array of arguments of the specified type. You also can send no arguments. If you send no arguments, the length of the params list is zero.

No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration.

public static void Test(params string[] strings)
{
}

Test(string1)
Test(string1, string2)

依此类推 string1 ... stringN.

不错的发现!

Is this undefined behavior?

没有。这是设计使然的行为。奇怪的设计,但设计使然。

Does that depend on the .Net framework version / Mono version?

没有。所有版本的 C# 都有此行为。

这是一些有趣的 C# 规则冲突的结果。

第一个相关规则是:可以以 "normal" 或 "expanded" 形式调用具有参数数组的方法。正常形式就好像没有 "params"。扩展形式获取参数并将它们捆绑到一个自动生成的数组中。 如果两种形式都适用,则普通形式优于扩展形式

现在,这似乎是明智的;如果您手头有一个对象数组,您很可能想要传递对象数组,而不是包含对象数组的数组。

第二个相关规则是当元素类型是引用类型时,C# 允许不安全的数组协变。也就是说,字符串数组可以隐式转换为对象数组。您会注意到这有两个含义。首先,这意味着当您有一个对象数组时,它可能 实际上 是一个字符串数组,因此将海龟放入该对象数组可能会导致类型错误。这是非常令人惊讶的!您希望每个对象数组都可以采用任何对象,但在 C# 中并非如此。一些对象数组在说谎。

第二个含义是:既然将乌龟放入真正的字符串数组中,则必须抛出异常,这意味着 每次将某些内容放入基本类型的数组中时,运行时都必须验证类型检查。因此,在 C# 中,数组写入在 每次 写入时都非常昂贵,因此可以捕获极少数的不良写入。

这是一团糟,这就是为什么不安全的数组协变在我的不幸 C# 功能列表中名列前茅。

这两个规则的组合解释了您的观察结果。字符串数组可以转换为对象数组,因此该方法适用于普通形式。

对于整数数组,协方差不适用于值类型。所以整数数组不能转换为对象数组,因此该方法不适用于其正常形式。但是整数数组是一个对象,所以它适用于扩展形式。

另请参阅:

Why does params behave like this?

你的问题可以说是重复的:

Is there a way to distingish myFunc(1, 2, 3) from myFunc(new int[] { 1, 2, 3 })?