这是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#编译器错误?不常见......)
答案 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节),或
- [...有关
ref
和out
参数的一些无关材料......]对于包含参数数组的函数成员,如果函数成员适用于上述规则,则称其适用于其正常形式。如果包含参数数组的函数成员不适用于其正常形式,则函数成员可以适用于其扩展形式[。]
因为您传递的数组可以隐式转换为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!