There is an exercise "OverloadResolutionOverride"
What will be the output of the following code:
class Foo
{
public virtual void Quux(int a)
{
Console.WriteLine("Foo.Quux(int)");
}
}
class Bar : Foo
{
public override void Quux(int a)
{
Console.WriteLine("Bar.Quux(int)");
}
public void Quux(object a)
{
Console.WriteLine("Bar.Quux(object)");
}
}
class Baz : Bar
{
public override void Quux(int a)
{
Console.WriteLine("Baz.Quux(int)");
}
public void Quux<T>(params T[] a)
{
Console.WriteLine("Baz.Quux(params T[])");
}
}
void Main()
{
new Bar().Quux(42);
new Baz().Quux(42);
}
There answer is:
Bar.Quux(object)
Baz.Quux(params T[])
There is an explanation on the site:
If compiler found a suitable signature for a method
call in the “current” class, compiler will not look to parents
classes.
Is it considered that the overloaded Quux(int) method is in the base class, and not in the current one? If so, how i can call exactly Quux(int) method in current class?
This is definitely an interesting effect of what happens when the compiler does its method resolution.
Let's go to the specification and see what we can get out of that (This is my understanding and I am by no means an expert in reading the specification, so if I got something wrong, please let me know!).
The first step is to find methods that have parameters that are Applicable with the first bullet reading:
Each argument in A [Where A is the argument list] corresponds to a parameter in the function member
declaration as described in Corresponding parameters, and any
parameter to which no argument corresponds is an optional parameter.
So now we go check out what a Corresponding parameter is and we get:
For virtual methods and indexers defined in classes, the parameter
list is picked from the most specific declaration or override of the
function member, starting with the static type of the receiver, and
searching through its base classes.
And we also have
For all other function members and delegates there is only a single
parameter list, which is the one used.
Therefore, for the class Bar
, we find two methods the fit the bill:
Bar.Quux(object)
. This is from the second paragraph because it is defined on the type directly.
Foo.Quux(int)
. This is from the derived type by following the override up to the virtual
method declaration.
For the Baz
class we get 3:
Baz.Quux(int[])
. This is defined on the type.
Bar.Quux(object)
. This is defined in a parent class and is visible to the scope of Baz
.
Foo.Quux(int)
. This is the virtual declaration of the override.
This gives us 2 method matches in Bar
, and 3 possible method matches in Baz
. This means we need to do further culling of the parameter set with the following next set (Emphasis mine):
The set of candidate methods is reduced to contain only methods from
the most derived types: For each method C.F in the set, where C is the
type in which the method F is declared, all methods declared in a base
type of C are removed from the set. Furthermore, if C is a class type
other than object, all methods declared in an interface type are
removed from the set. (This latter rule only has affect when the
method group was the result of a member lookup on a type parameter
having an effective base class other than object and a non-empty
effective interface set.)
Therefore, for Bar
, we will cull Foo.Quux(int)
because it is declared in a base type and is therefore removed from the set.
For Baz
, we remove the following two methods because they are both declared in base types:
Bar.Quux(object)
Foo.Quux(int)
Now, each set has only one method, and we can execute the two methods Bar.Quux(object)
and Baz.Quux<int>(int[])
.
Picking the right Method
So this begs the question, can we force the correct method to be called? And based on the second resolution step, where it uses the most derived type, the answer is that yes we can.
If we want to call Foo
's methods, we need to set the type of our caller to Foo
. If we want Baz
to call Bar
's method, then we will need to set Baz
to Bar
.
Considering the following set of method calls:
new Bar().Quux(42);
new Baz().Quux(42);
((Foo)new Bar()).Quux(42);
((Foo)new Baz()).Quux(42);
((Bar)new Baz()).Quux(42);
We get the following output:
Bar.Quux(object)
Baz.Quux(params T[])
Bar.Quux(int)
Baz.Quux(int)
Bar.Quux(object)
And are able to target the specific methods that we want to using a similar method resolution as above.
One More Step
If we change the definition of Baz
to be following:
class Baz : Bar
{
public override void Quux(int a)
{
Console.WriteLine("Baz.Quux(int)");
}
}
And then make the method call: new Baz().Quux(42);
our output is still: Bar.Quux(object)
. This seems strange because of the fact that we have a method override defined within Baz
directly. However, the originating type for the method is Foo
which is less specific than Bar
. So when we match our parameter lists, we end up with Bar.Quux(object)
and Foo.Quux(int)
since the int
parameter list is defined on Foo
. Therefore, Foo.Quux(int)
is culled in the second step since Bar
is more derived than Foo
, and we call Bar.Quux(object)
.
I think the moral of the story here is don't name methods the same as overrides!
As other note, the way to call the method you wan't is to downcast: (Bar)(new Baz).Quux(42);
.
But why does the compiler choose the generic method? Isn't an exact non generic match preferred over a generic method when resolving method resolution?
Yes, but another rule applies here too; method resolution will prefer the nearest applicable method, nearest meaning where the method is declared in relation to the callsite.
Uhm... Quux(int)
is declared in Baz
so I'm not really sure...
No! Overriden methods belong to the class that implements the virtual method; in this case, as far as the compiler is concerned, the "owner" of Quux
is Foo
, not Baz
.
And why would the compiler apply such a rule? Well, suppose the following scenario:
Company Alpha publishes a class A
:
class A { ... }
And company Beta goes ahead and consumes A
by extending it with B
:
class B: A {
public void Frob<T>(T frobbinglevel) { ... }
... }
Everything is honky dory and Beta is having tremendous success with its brilliant frobbing. Company Alpha decides to have a go with its own frobbing and publishes a new version of A
:
class A {
public virtual void Frob(int frobbinglevel) { ... }
... }
Beta recompiles its library with Alpha's updates and now what should happen? Should the compiler start choosing A.Frob(1)
over B.Frob(1)
, A.Frob
is a non generic exact match?
But hey... Beta hasn't changed anything and now its class is not working as it should! Suddenly, without any change in its code, method resolution is choosing different methods, breaking its customers...that doesn't seem right. This is the reason why the rule exists.
To call exactly Quux(int) from Bar or Baz you should cast Bar or Baz to Foo:
Foo bar = new Bar();
Foo baz = new Baz();
bar.Quux(42);
baz.Quux(42);
The output:
Bar.Quux(int)
Baz.Quux(int)