在方法覆盖中更改params修饰符

我知道params修饰符(将数组类型的一个参数转换成所谓的“参数数组”)特别不是方法签名的一部分。 现在考虑这个例子:

 class Giraffid { public virtual void Eat(int[] leaves) { Console.WriteLine("G"); } } class Okapi : Giraffid { public override void Eat(params int[] leaves) { Console.WriteLine("O"); } } 

编译时没有任何警告。 然后说:

 var okapi = new Okapi(); okapi.Eat(2, 4, 6); // will not compile! 

给出一个错误( No overload for method 'Eat' takes 3 arguments )。

现在,我知道编译器将params修饰符转换为System.ParamArrayAttribute的应用程序到相关参数上。 通常,将一个属性集合应用于虚方法的参数,然后在具有不同属性集的派生类中的重写方法中装饰“对应”参数是没有问题的。

然而编译器选择静默忽略我的params关键字。 相反,如果将其反过来,并将params应用于基类Giraffid的参数,然后在Okapi中忽略覆盖中的关键字,则编译器选择使用System.ParamArrayAttribute修饰这两个方法。 当然,我用IL DASMvalidation了这些东西。

我的问题:

这是记录在案的行为? 我已经彻底搜索了C#语言规范,但没有发现任何提及。

我可以说至少Visual Studio开发环境对此感到困惑。 在上面的方法调用中输入2, 4, 6时, intellisense提示中显示void Okapi.Eat(params int[] leaves)


为了比较,我还尝试实现接口方法并在接口和实现类中更改params的存在/不存在,我尝试在委托类型定义或我分配的方法组的方法中定义委托类型和更改params或不更改params到我的委托类型的变量。 在这些情况下,完全可以改变params -ness。

编译器的行为是正确的,但这有点乱。 我希望这至少是一个警告。

毫无疑问,你无法在规范中找到它说的这是正确的。 相关位是:

forms为M(A)的方法调用的绑定时处理,其中M是方法组,A是可选的参数列表,由以下步骤组成:构造方法调用的候选方法集合。 对于与方法组M相关联的每个方法F,如果F是非generics的,则当M没有类型参数列表时F是候选,并且F适用于A.

什么是“与方法组M相关的方法”? 好吧,首先,什么是方法组?

方法组,它是由成员查找产生的一组重载方法…

好的,那么成员查找规则是什么?

否则,该集由T中名为N的所有可访问成员组成,包括inheritance成员和对象中名为N的可访问成员。 包含覆盖修饰符的成员将从集合中排除。

强调补充说。

这里的实际结果是, 为了重载解析的目的,重写的方法被认为是最初声明的方法,而不是覆盖的方法。 不幸的是,在这种情况下违反了这条规则:

 virtual void M(int x, int y) { } ... override void M(int y, int x) { } ... M(x = 1, y = 2); 

重载解析使用更多派生版本中的名称 。 这是一个令人遗憾的结果,因为命名参数在游戏中很晚才被添加。

简而言之:为了确定方法是否为“参数”,分析是在原始方法上进行的 ,而不是在重写方法上进行的

如果编译器在这里给你一个警告,那就太好了。

可以说至少Visual Studio开发环境对此感到困惑

正确。 IntelliSense图层始终显示覆盖方法的方法信息,而不是重写方法。 研究表明,当方法看起来好像它们是最初的声明方法而不是最重要的方法时,用户发现它令人困惑。 当然,正如我之前提到的那样,那些是你将用于命名参数的参数名称。

我认为它是在c#规范的1.6.6.4段中描述的:

可以在派生类中重写虚方法。 当实例方法声明包含override修饰符时,该方法将覆盖具有相同签名的inheritance虚拟方法。 虚拟方法声明引入了新方法,而覆盖方法声明通过提供该方法的新实现来专门化现有的inheritance虚拟方法。

据此, virtual方法声明在这里非常重要。 并且每次调用该方法时都会使用virtual方法声明。 正确override n实现(如果指定)在运行时进行,其中params完全没有任何关系。

可以通过简单的测试来确认:

 class Giraffid { public virtual void Eat(params int[] leaves) { Console.WriteLine("G"); } } class Okapi : Giraffid { public override void Eat(int[] leaves) { Console.WriteLine("O"); } } 

有了那个宣言

 var o = new Okapi(); o.Eat(1, 2, 3); 

工作100%罚款。