更改方法重写中的params修饰符

本文关键字:params 方法 重写 | 更新日期: 2023-09-27 18:07:12

我知道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 DASM验证了这些东西。

我的问题:

这是有记录的行为吗?我已经彻底搜索了C#语言规范,没有发现任何提及这一点的地方

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


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

更改方法重写中的params修饰符

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

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

形式为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",分析是在原始方法上进行的,而不是在override方法上进行的。

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

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

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

我认为它在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%良好。