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?
To call exactly Quux(int) from Bar or Baz you should cast Bar or Baz to Foo:
The output:
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 inBaz
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
isFoo
, notBaz
.And why would the compiler apply such a rule? Well, suppose the following scenario:
Company Alpha publishes a class
A
:And company Beta goes ahead and consumes
A
by extending it withB
: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
:Beta recompiles its library with Alpha's updates and now what should happen? Should the compiler start choosing
A.Frob(1)
overB.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.
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:
So now we go check out what a Corresponding parameter is and we get:
And we also have
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 thevirtual
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 ofBaz
.Foo.Quux(int)
. This is the virtual declaration of the override.This gives us 2 method matches in
Bar
, and 3 possible method matches inBaz
. This means we need to do further culling of the parameter set with the following next set (Emphasis mine):Therefore, for
Bar
, we will cullFoo.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)
andBaz.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 toFoo
. If we wantBaz
to callBar
's method, then we will need to setBaz
toBar
.Considering the following set of method calls:
We get the following output:
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: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 withinBaz
directly. However, the originating type for the method isFoo
which is less specific thanBar
. So when we match our parameter lists, we end up withBar.Quux(object)
andFoo.Quux(int)
since theint
parameter list is defined onFoo
. Therefore,Foo.Quux(int)
is culled in the second step sinceBar
is more derived thanFoo
, and we callBar.Quux(object)
.I think the moral of the story here is don't name methods the same as overrides!