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

Jep*_*sen 16 c# overriding virtual-functions params-keyword 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var okapi = new Okapi();
okapi.Eat(2, 4, 6);  // will not compile! 
Run Code Online (Sandbox Code Playgroud)

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

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

然而,编译器选择params静默忽略我的关键字.相反,如果一个使它反过来,并且应用于params基类中的参数Giraffid,然后省略覆盖中的关键字Okapi,编译器选择用它来装饰两个方法System.ParamArrayAttribute.当然,我用IL DASM验证了这些东西.

我的问题:

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

我可以说至少Visual Studio开发环境对此感到困惑.当键入2, 4, 6在上述方法的调用,所述智能感知显示我void Okapi.Eat(params int[] leaves)于尖端.


为了比较,我也尝试实现一个接口方法并改变params接口和实现类的存在/不存在,我尝试params在委托类型定义或我分配给它的方法组的方法中定义委托类型和改变与否.我的委托类型的变量.在这些情况下,完全有可能改变params- 性.

Eri*_*ert 16

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

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

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

什么是"与方法组M相关的方法"?那么,首先,什么是方法组?

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

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

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

强调补充说.

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

virtual void M(int x, int y) { }
...
override void M(int y, int x) { } 
...
M(x = 1, y = 2);
Run Code Online (Sandbox Code Playgroud)

重载决策使用的名字多个衍生版本.这是一个令人遗憾的结果,即在游戏中很晚才添加了命名参数.

简而言之:为了确定方法是否为"参数",分析是在原始方法上进行的,而不是在最重要的方法上进行的.

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

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

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

  • 请注意,这也解释了为什么它只适用于接口(示例代码一式两份,在http://stackoverflow.com/questions/27843804/compiling-generic-interface-vs-generic-abstract-class-params-keyword) - 有没有"覆盖",所以带有`params`*的方法不会从可能的匹配列表中排除*. (2认同)

Mar*_*zek 5

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

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

据此,virtual方法声明在这里非常重要.和virtual方法声明用于在每次调用该方法.正确的overriden个实现(如果指定)是在运行时获取的,其中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");
    }
}
Run Code Online (Sandbox Code Playgroud)

有了那个宣言

var o = new Okapi();
o.Eat(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

工作100%罚款.