Consider the following code (it's a little long, but hopefully you can follow):
class A { } class B : A { } class C { public virtual void Foo(B b) { Console.WriteLine("base.Foo(B)"); } } class D: C { public override void Foo(B b) { Console.WriteLine("Foo(B)"); } public void Foo(A a) { Console.WriteLine("Foo(A)"); } } class Program { public static void Main() { B b = new B(); D d = new D (); d.Foo(b); } }
If you think the output of this program is "Foo(B)" then you'd be in the same boat as me: completely wrong! In fact, it outputs "Foo(A)"
If I remove the virtual method from the C
class, then it works as expected: "Foo(B)" is the output.
Why does the compiler choose the version that takes a A
when B
is the more-derived class?
It is not possible for these functions to get overloaded.
What Does Virtual Method Mean? A virtual method is a declared class method that allows overriding by a method with the same derived class signature. Virtual methods are tools used to implement the polymorphism feature of an object-oriented language, such as C#.
Basically, a virtual function is used in the base class in order to ensure that the function is overridden. This especially applies to cases where a pointer of base class points to an object of a derived class.
A virtual method is one that is declared as virtual in the base class. A method is declared as virtual by specifying the keyword "virtual" in the method signature. A virtual method may or may not have a return type. Virtual methods allow subclasses of the type to override the method.
The answer is in the C# specification section 7.3 and section 7.5.5.1
I broke down the steps used for choosing the method to invoke.
First, the set of all accessible members named N (N=Foo
) declared in T (T=class D
) and the base types of T (class C
) is constructed. Declarations that include an override modifier are excluded from the set (D.Foo(B) is exclude)
S = { C.Foo(B) ; D.Foo(A) }
The set of candidate methods for the method invocation is constructed. Starting with the set of methods associated with M, which were found by the previous member lookup, the set is reduced to those methods that are applicable with respect to the argument list AL (AL=B
). The set reduction consists of applying the following rules to each method T.N in the set, where T (T=class D
) is the type in which the method N (N=Foo
) is declared:
If N is not applicable with respect to AL (Section 7.4.2.1), then N is removed from the set.
C.Foo(B)
is applicable with respect to ALD.Foo(A)
is applicable with respect to AL
S = { C.Foo(B) ; D.Foo(A) }
If N is applicable with respect to AL (Section 7.4.2.1), then all methods declared in a base type of T are removed from the set. C.Foo(B)
is removed from the set
S = { D.Foo(A) }
At the end the winner is D.Foo(A)
.
If the abstract method is removed from C, the initial set is S = { D.Foo(B) ; D.Foo(A) }
and the overload resolution rule must be used to select the best function member in that set.
In this case the winner is D.Foo(B)
.
Why does the compiler choose the version that takes a A when B is the more-derived class?
As others have noted, the compiler does so because that's what the language specification says to do.
This might be an unsatisfying answer. A natural follow-up would be "what design principles underly the decision to specify the language that way?"
That is a frequently asked question, both on StackOverflow and in my mailbox. The brief answer is "this design mitigates the Brittle Base Class family of bugs."
For a description of the feature and why it is designed the way it is, see my article on the subject:
http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx
For more articles on the subject of how various languages deal with the Brittle Base Class problem see my archive of articles on the subject:
http://blogs.msdn.com/b/ericlippert/archive/tags/brittle+base+classes/
Here's my answer to the same question from last week, which looks remarkably like this one.
Why are signatures declared in the base class ignored?
And here are three more relevant or duplicated questions:
C# overloading resolution?
Method overloads resolution and Jon Skeet's Brain Teasers
Why does this work? Method overloading + method overriding + polymorphism
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With