C#params明显的编译器错误(C#5.0)

时间:2012-03-14 20:20:00

标签: c# params c#-5.0

这是thread的后续行动,我认为昨天已经解决了。昨天我在以下情况下遇到了我的代码问题:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    class Program
    {
        class Bar
        {
            int v;

            public Bar(int v) { this.v = v; }
            public override string ToString() { return v.ToString(); }
        }

        static void Main(string[] args)
        {
            Foo(1, 2, 3);
            Foo(new int[] { 1, 2, 3 });
            Foo(new Bar(1), new Bar(2), new Bar(3));
            Foo(new Bar[] { new Bar(1), new Bar(2), new Bar(3) });
            System.Threading.Thread.Sleep(20000);
        }

        static void Foo(params object[] objs)
        {
            Console.WriteLine("New call to Foo: ");
            foreach(object o in objs)
                Console.WriteLine("Type = " + o.GetType() + ", value = "+o.ToString());
        }
    }
}

如果您运行此操作,您可以看到上次调用Foo时出现问题。参数是向量的事实是“丢失”。

所以....有谁知道如何报告C#编译器错误?或者这会被视为反射错误?

(真是令人宽慰:我很沮丧地认为我浪费了时间来处理我自己的错误。实际上它毕竟是一个C#错误,我已经得到了证实!我们经常看到实际的错误这些天C#编译器错误?不常见......)

5 个答案:

答案 0 :(得分:18)

C# 4.0 specification非常清楚这一切是如何发挥作用的。 7.5.3.1表示如果具有params的函数可以以正常形式(忽略params关键字)或扩展形式(使用params关键字)应用,则普通形式获胜。

假设Foo被声明为Foo(params object[] args),则调用Foo(new Foobar[] {new Foobar(1), new Foobar(2), new Foobar(3)) });以正常形式适用,因为Foobar[]可隐式转换为object[](6.1。 6第5条)。因此,使用普通形式,忽略扩展形式。

(我假设C#5.0没有改变这部分语言。)

答案 1 :(得分:7)

我希望期望这两个调用功能相同 - params参数是被调用方法中的数组。 Jon Skeet在前一个问题中的示例有效,因为int的数组与对象数组不协变(因此被视为new Object[] { new Int[] {1,2,3} }),但在此示例中,FooBars 的数组是< / em> 协变到一个对象数组,因此您的参数扩展为objs参数。

所有事物的维基百科涵盖了这个确切的案例:Covariance and contravariance (computer science)

抱歉,我确信这不是编译器错误。

编辑:

你可以达到你想要的效果:

Foo(new Object[] { new Bar[] { new Bar(1), new Bar(2), new Bar(3) } });

NEW EDIT(其他作者):

或者只是使用:

Foo((Object)new Bar[] { new Bar(1), new Bar(2), new Bar(3) });

答案 2 :(得分:3)

参见C#规范的第7.5.3.1节:

  

7.5.3.1适用的功能成员

     

当满足以下所有条件时,函数成员被称为关于参数列表A的适用函数成员:

     
      
  • A中的每个参数对应于§7.5.1.1中描述的函数成员声明中的参数,并且任何无参数对应的参数都是可选参数。
  •   
  • 对于A中的每个参数,参数的参数传递模式(即value,ref或out)与相应参数的参数传递模式相同,并且   
        
    • 对于值参数或参数数组,从参数到相应参数的类型存在隐式转换(第6.1节),或
    •   
    • [...有关refout参数的一些无关材料......]
    •   
  •   
     

对于包含参数数组的函数成员,如果函数成员适用于上述规则,则称其适用于其正常形式。如果包含参数数组的函数成员不适用于其正常形式,则函数成员可以适用于其扩展形式[。]

因为您传递的数组可以隐式转换为object[],并且因为重载解析比“扩展”形式更喜欢“正常”形式,所以您观察到的行为符合规范,并且没有错误。< / p>

除了Chris Shain描述的解决方法之外,您还可以将Bar从类更改为结构;这使得数组不再可以隐式转换为object[],因此您将获得所需的行为。

答案 3 :(得分:2)

我相信你错了肯 那是因为int []不是object []类型,所以编译器假定int []只是传递给方法的参数之一。

这是如何:

new Foobar[] { } is object[]; // true
new int[] { } is object[]; // false

更新: 你可以让方法通用,让编译器知道struct / object类型传递为params:

void Foo<T>(params T[] objs)
{
    foreach (T o in objs)
        Console.WriteLine(o.GetType());
}

答案 4 :(得分:-6)

所以我建议最好的答案是我是对的,这确实是一个编译错误。虽然我清楚地看到了这一点,但您的解释忽略了“params”关键字。事实上,要以这种方式使用协方差,必须忽略params关键字,就好像它不重要一样。我假设没有合理的解释来编译代码这种方式,Params作为Foo类型签名上的关键字出现:要调用你的解释,你需要说服我myClass []应该与object []类型匹配但是,鉴于params构造,我们甚至不应该问这个问题。事实上,您对此的想法越多,它就越清楚它实际上是一个真正的C#5.0编译器错误:编译器忽略了应用params关键字。语言规范实际上根本不需要任何改变。我应该得到某种奇怪的徽章,imho!