C# 在方法替代中更改参数修改器

C# 在方法替代中更改参数修改器,c#,overriding,params,virtual-functions,params-keyword,C#,Overriding,Params,Virtual Functions,Params Keyword,我知道params修饰符(将数组类型的一个参数转换为所谓的“参数数组”)并不是方法签名的一部分。现在考虑这个例子: class Giraffid { public virtual void Eat(int[] leaves) { Console.WriteLine("G"); } } class Okapi : Giraffid { public override void Eat(params int[] leaves) {

我知道
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! 
给出一个错误(
方法“Eat”不重载3个参数

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

但是编译器选择默默地忽略我的
params
关键字。相反,如果一个方法反过来,将
params
应用于基类
Giraffid
中的参数,然后在
Okapi
中忽略覆盖中的关键字,编译器将选择使用
System.paramaryattribute
来修饰这两个方法。当然,我和伊尔·达斯姆核实了这些

我的问题:

这是记录在案的行为吗?我已经彻底搜索了C语言规范,没有发现任何关于这一点的内容

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



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

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

可以在派生类中重写虚方法。当 实例方法声明包括一个覆盖修饰符,即 使用相同的签名重写继承的虚拟方法鉴于 虚方法声明引入了一个新方法,即重写 方法声明通过 提供该方法的新实现。

因此,
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%正常。

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

毫不奇怪,您无法在规范中找到它说这是正确的地方。相关位为:

形式为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);
重载解析使用派生版本中的名称。这是一个不幸的结果,因为命名参数是在游戏很晚的时候添加的

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

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

可以说,至少VisualStudio开发环境对此感到困惑


对。IntelliSense层始终显示重写方法的方法信息,而不是被重写的方法。研究表明,当方法看起来像是最初声明的方法,而不是重写方法时,用户会感到困惑。当然,正如我前面提到的,这些是您将用于命名参数的参数名。

请注意,这也回答了为什么它只适用于接口(中的示例代码一式两份)-没有“覆盖”,因此带有
params的方法不排除在可能的匹配列表中。