Changing the params modifier in a method override

耗尽温柔 提交于 2019-11-30 06:50:55
Eric Lippert

The compiler's behaviour is correct, but this is a bit of a mess. I would have preferred this to be at least a warning.

It is unsurprising that you can't find where in the spec it says this is correct. The relevant bits are:

The binding-time processing of a method invocation of the form M(A), where M is a method group, and A is an optional argument-list, consists of the following steps: The set of candidate methods for the method invocation is constructed. For each method F associated with the method group M, if F is non-generic, F is a candidate when M has no type argument list, and F is applicable with respect to A.

What are the "methods associated with the method group M"? Well, first off, what is a method group?

A method group, which is a set of overloaded methods resulting from a member lookup...

OK, so what are the member lookup rules?

Otherwise, the set consists of all accessible members named N in T, including inherited members and the accessible members named N in object. Members that include an override modifier are excluded from the set.

Emphasis added.

The practical upshot here is that for the purpose of overload resolution, an overridden method is considered to be the method that was originally declared, not the method that was overriding. This rule is, unfortunately, violated in this case:

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

Overload resolution uses the names from the more derived version. This is an unfortunate consequence of the fact that named arguments were added very late in the game.

In short: for the purposes of determining whether a method is "params" or not, the analysis is done on the original method, not on the overriding method.

It would have been nice had the compiler given you a warning here.

can say that at least the Visual Studio development environment is confused about this

Correct. The IntelliSense layer always displays the method information for the overriding method, not the overridden method. Research showed that users found it confusing when methods were made to look as though they were the originally declaring method, not the overriding method. And of course, as I mentioned before, those are the parameter names you're going to use for named arguments.

I think it's described in 1.6.6.4 paragraph of c# specification :

A virtual method can be overridden in a derived class. When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

According to that, virtual method declaration is really important here. And the virtual method declaration is used on every call to that method. Correct overriden implementations (if specified) are taken in runtime, where params has nothing to do at all.

It can be confirmed by simple test:

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

With that declaration

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

works 100% fine.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!