【问题标题】:C# params object[] strange behaviorC# params object[] 奇怪的行为
【发布时间】:2016-03-08 19:35:15
【问题描述】:

考虑到这段代码

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[],我得到一个 object[],其中 int[] 作为第一个元素。

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

【问题讨论】:

标签: c# arrays casting params


【解决方案1】:

很好的发现!

这是未定义的行为吗?

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

这是否取决于 .Net 框架版本/Mono 版本?

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

这是一些有趣的 C# 规则碰撞的结果。

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

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

第二条相关规则是,当元素类型是引用类型时,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 })?

【讨论】:

  • 信息不能来自更好的来源,伟大的见解埃里克
  • 谢谢。我做了更多的测试,似乎我们传递一个“类对象”数组的行为有所不同:与字符串相同的行为,当我们传递一个“结构对象”数组时:我们得到与 int 相同的行为。
  • @evg02gsa3:正确。 T 是引用类型的所有T[] 都可以转换为object[],但该协方差仅适用于引用类型。如果您有引用类型AnimalGiraffe,则Giraffe[] 可转换为Animal[],但int[] 不可转换为ValueType[]object[]
  • @EricLippert 感谢您在这里的明确解释! ;) 此外,您的博文既 particularly entertaining 又棒极了! ;) 继续写!
  • @Evk:感谢您的留言。我看了看。基本问题非常简单,但事实证明发帖人可能在重载解析和属性处理的交叉点中发现了一个错误。有趣的问题!
【解决方案2】:

我不是专家,但 params 关键字的想法是使您能够对方法进行不同的调用,而与您拥有多少元素无关。

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

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

通过使用 params 关键字,您可以指定一个方法参数 接受可变数量的参数。

您可以发送以逗号分隔的参数声明中指定类型的参数列表或指定类型的参数数组。您也可以不发送任何参数。如果您不发送任何参数,则参数列表的长度为零。

params 关键字后面不允许有其他参数 在方法声明中,并且只允许使用一个 params 关键字 方法声明。

【讨论】:

  • 好吧,如果我使用 int[] 而不是 string[] 看起来行为会有所不同。这很奇怪。我将编辑我的原始帖子。
【解决方案3】:
public static void Test(params string[] strings)
{
}

Test(string1)
Test(string1, string2)

以此类推 string1 ... stringN。

【讨论】:

    猜你喜欢
    • 2012-08-28
    • 1970-01-01
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 2019-04-10
    相关资源
    最近更新 更多