Was looking at some code in our codebase and I'm unable to understand how/why this is even working (and not causing a stackoverflow due to infinite recursion). I have pasted some equivalent code below:
We have a virtual method Foo(B) defined in class P1 and overridden in class P2. P2 also defines a private non-virtual method Foo(A). B derives from A. P2::Foo(B) has a call in the end: Foo(b). I expect this to end up in a stack overflow.
However, the output is:
P2::Foo Virtual
P2::Foo Private Non-Virtual
Looks like the second call to Foo in the overridden method is picking up the non-virtual method Foo in this case. On doing similar operations in P1 (uncomment code), we end up calling Foo infinite number of times through recursion.
Questions: (finally!)
1. Why is the behavior different in the original virtual method and the overridden method? Why is one calling itself and the other calling a different method?
2. Is there an order of preference specified somewhere? Note that if we change the private modifier to public, in both cases, we end up calling the non-virtual method (Even if we instantiate P2 this way: P1 p2 = new P2(); , instead of P2 p2 = new P2();) It looks like the non-virtual version is preferred, except when it is inside a virtual method definition. Is this true?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class P1
{
static void Main(string[] args)
{
B b = new B();
P2 p2 = new P2();
p2.Foo(b);
// Uncomment code to cause infinite recursion
//P1 p1 = new P1();
//p1.Foo(b);
}
private void Foo(A a)
{
Console.WriteLine("P1::Foo Private Non-Virtual");
}
public virtual void Foo(B b)
{
Console.WriteLine("Inside P1::Foo");
// Uncomment code to cause infinite recursion
// Foo(b);
}
}
public class P2 : P1
{
private void Foo(A a)
{
Console.WriteLine("P2::Foo Private Non-Virtual");
}
public override void Foo(B b)
{
Console.WriteLine("P2::Foo Virtual");
Foo(b);
}
}
public class A
{
public int a = 10;
}
public class B : A
{
public int b = 20;
}
}
This is because overload resolution only looks at inherited members if it cannot select an overload defined on the derived type. From the spec (version 4):
For example, the set of candidates for a method invocation does not include methods marked override (§7.4), and methods in a base class are not candidates if any method in a derived class is applicable (§7.6.5.1).
To address your questions specifically:
Why is the behavior different in the original virtual method and the overridden method?
Because the overridden method is defined in a derived class, and an applicable overload exists in that class, the virtual method is not considered. The overriding method is not considered, because overrides are never considered.
Why is one calling itself and the other calling a different method?
The behavior in the derived class is explained above. In the base class, the best candidate for overload resolution is the virtual method itself, because it is more specific (B is derived from A).
Is there an order of preference specified somewhere?
Yes, in the C# Language Specification (link to the MSDN page for the Visual Studio 2012 version of the specification).
Note that if we change the private modifier to public, in both cases, we end up calling the non-virtual method (Even if we instantiate P2 this way: P1 p2 = new P2(); , instead of P2 p2 = new P2();)
Accessibility is not a significant issue in this case. The type of the variable p2 is not relevant either, because the overload resolution you're asking about concerns a call site in the P2 override of the virtual method. Virtual dispatch ensures that the call in Main() invokes the override, regardless of the static type of the variable. At the call site in P2's override void Foo(B b), the receiver is implicitly this, which always has a static type of P2.
It looks like the non-virtual version is preferred, except when it is inside a virtual method definition. Is this true?
Not quite; as explained above, the preference is not for non-virtual methods, but for methods defined in the type of the receiver (i.e., the static type of the object reference on which the method is being called).
This is a frequently misunderstood feature of C#: methods in a base class are not candidates if any method in a derived class is applicable
Related
Why does this program print "abc from B" instead of "abc from A"? My intuition says that it should resolve the method to the more specific type parameter (which is how I've always used overloading).
public class Program
{
public static void Main(string[] args)
{
int i = 5;
B b = new B();
b.Method1(i);
Console.ReadLine();
}
}
class A
{
public void Method1(int q)
{
Console.WriteLine("abc from A");
}
}
class B : A
{
public void Method1(double p)
{
Console.WriteLine("abc from B");
}
}
Overload resolution doesn't work across types.
This is due to one crucial step in determining the set of candidate methods for overload resolution, as stated in the language spec:
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.
Basically, the A.Method1 isn't even considered for overload resolution.
There are a few other steps to this, but the spec nicely summarises the process:
To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected.
Interestingly, your intuition that A.Method1 should be called because there is an identity conversion from int to int is how it works in Java :)
Because you called it from a variable of the B type. If you cast b as an A, you'd get the first result. See it here:
https://dotnetfiddle.net/TAYfJX
If you want it to do overloading based on argument type, put both versions of the method in the same type.
I am wondering if possible to override a method from calling console app or a super super class? I understand I can override WriteLog at DoMath.... however consider that I would like to manage this instead at the console app.
Example:
public class LogThings
{
public virtual void WriteLog(string value)
{
Console.WriteLine("This is the base in LogThings " + value);
}
}
The class inheriting the base. I kind of thought if I add the method again and mark it with new to implement as a virtual, then I can override this in the console app that inherits DoMath?
public class DoMath : LogThings
{
public double DoAddition(double a, double b)
{
double result;
result = a + b;
WriteLog(result.ToString()); // < the operation I need to overload
return result;
}
public new virtual void WriteLog(string value)
{
Console.WriteLine("this is overriding the base in DoMath");
base.WriteLog(value);
}
}
Some console app using the doMathANdLog class library:
class Program : DoMath
{
static void Main(string[] args)
{
var m = new DoMath();
m.DoAddition(1, 2);
Console.ReadLine();
}
public override void WriteLog(string value)
{
Console.WriteLine("this is not overriding.");
}
}
Result is this when running it:
this is overriding the base in DoMath
This is the base in LogThings 3
Is there a way to do this?
That's a simultaneously fun and annoying problem to have! :) I believe that one of the missing links here is this:
The override modifier is related to virtual or abstract methods; a method with an override modifier simply provides an alternative implementation to an existing virtual or abstract method. A virtual method has a default implementation, an abstract method has no implementation at all.
The new modifier however can be applied to any method and simply allows the reuse of a name that was already taken. These methods are not related to each other at all, the only similarity being that they forcibly share the same name.
The problem with new is that the type using a new method "pretends" that the original implementation never existed. The base class however is absolutely unaware of this - new severs the link in the hierarchy, and this is what's casing your problem.
Generally speaking, you do not want to use the new modifier.
That, and you probably wanted to use var m = new Program(); instead - reasons being explained below.
Consider these two pieces of code:
LogThings a = new DoMath();
a.WriteLog("something");
and
LogThings a = new Program();
a.WriteLog("something");
At this point, the method being called is LogThings.WriteLog(). Even though we instantiate a DoMath or Program class that provides a new method, the LogThings part of the world doesn't "know" that. It instead believes to have a virtual method that doesn't happen to be overridden. As a result, this code prints:
This is the base in LogThings something
As mentioned above: new severs that link.
In the next example, the method being called is indeed DoMath.WriteLog(), because we're now simply instantiating the DoMath() class and call its LogThings method.
DoMath b = new DoMath();
b.WriteLog("something");
Not surprisingly, this code prints
this is overriding the base in DoMath
This is the base in LogThings something
Note that it does not print "this is not overriding" because we did not instantiate an instance of the Program class. Likewise, the base.LogThings() call has nothing to do with "overriding", it simply changes the focus to the LogThings type and calls whatever implementation it knows.
This is similar to the original code you used:
var m = new DoMath();
Lastly, consider this version:
DoMath c = new Program();
c.WriteLog("something");
Here, the Program class actually overrides the virtual void WriteLog method of DoMath. Consequently, this code prints
this is not overriding.
... which is now wrong, because it does.
The key to understanding this is that each class containing virtual or abstract methods has what's called a virtual function table, or vtable. This vtable is "inherited" by derived classes and allows the compiler to know which implementation of a method to call (through a so-called virtual dispatch).
You can consider an entry in the vtable to be something like a pointer to the actual implementation of a method (the one from the current class), followed by a pointer to the previous implementation.
In your example of DoMath and Program, instantiating the DoMath class would produce a vtable consisting of only
DoMath.WriteLog(string) -> null
whereas instantiating the Program class would produce an entry like this:
Program.WriteLog(string) -> DoMath.WriteLog(string) -> null
This is why ((DoMath)new Program()).WriteLog() works - even though we look at a DoMath reference, the compiler looks up the vtable for the instantiated type (Program) and can follow the chain up to the actual implementation (Program.WriteLog).
Do however note the makeshift null in there. Because the DoMath class declares the WriteLog method as new, it is considered to be a - well - new method, which is unrelated to the one from LogThings. For LogThings, the vtable world still looks somewhat like this:
LogThings.WriteLog(string) -> null
Because there is no legit override - just a different method that happens to have the same name - ((LogThings)new Program()).WriteLog() calls LogThings.WriteLog(), as that's the last implementation in the chain. The new essentially "forces in" another vtable, resulting in this somewhat split-brained setup.
Please note that this description of a vtable is drastically oversimplified; there's plenty of good material out there on that topic however.
As noted in the comments, DoMath provides a new implementation (it's in the definition there) of the WriteLog method. Your Program class then overrides that implementation.
Where it's 'failing' is here:
var m = new DoMath();
You're not creating an instance of Program with its overridden method, so that override is never used. Try new Program() instead.
My fret is: In the code presented below, it should display A then B. But it displays B then B. Why is it so?
What I feel is, constructor of A gets executed first when creating object of B. In that case, the method in B would not be hitted right? So it should be A.Display() and should result A. Also, then a.Display() should return B because we have override.
So I expect A then B. Because its not overloading but overriding. I know the definition of these things, I expect to understand the reason for this behavior and how it works internally as I am not convinced with BB but AB.
Code
class A
{
public A()
{
this.Display();
}
public virtual void Display()
{
Console.WriteLine("A");
}
}
class B :A
{
public override void Display()
{
Console.WriteLine("B");
}
}
class C
{
static void Main()
{
A a = new B();
a.Display();
Console.WriteLine();
Console.ReadLine();
}
}
Outputs
1) On override of Display method in the derived class yields the following:
A a = new A(); // ---> AA
B a = new B(); // ---> BB // I expect AB.
A a = new B(); // ---> BB // I expect AB.
2) using the NEW keyword in the Display method in derived class yields the following:
B a = new B(); // ---> AB // I Expect AA here.
A a = new B(); // ---> AA
A a = new A(); // ---> AA
3) More interesting findings are:
When I use base.Display() in the derived constructor with override of the base method in derived class, it gives me BAB
I do not see any logic at least in this. because, it should give BBB
What I feel is, constructor of A gets executed first when creating object of B.
Correct.
In that case, the method in B would not be hit right?
This is incorrect.
In similar code in C++ you would be correct. In C++ there is a rule that virtual function dispatch tables are built as the object is being constructed. That is, when the "A" constructor is entered, the vtable is filled in with the methods in "A". When control goes to the "B" ctor, the vtable is then filled in with the methods of B.
This is not the case in C#. In C# the vtable is filled in the moment the object comes out of the memory allocator, before either ctor is executed, and it does not change after that. The vtable slot for the method always contains the most derived method.
Therefore calling a virtual method in a ctor as you are doing here is a very bad idea. A virtual method can be called where the implementation is on a class whose ctor has not run yet! It might therefore depend on state that has not yet been initialized.
Note that field initializers run before all ctor bodies, so fortunately an override on a more derived class will always run after the field initializers of the overriding class.
The moral of the story is: simply don't do that. Don't ever call a virtual method in a ctor. In C++ you might get a different method than you expect, and in C# you might get a method that uses state that is not initialized. Avoid, avoid, avoid.
Why we shouldn't call virtual methods inside ctor? Is it because we get only the (latest derived) results only) always in the vtable?
Yes. Let me illustrate with an example:
class Bravo
{
public virtual void M()
{
Console.WriteLine("Bravo!");
}
public Bravo()
{
M(); // Dangerous!
}
}
class Delta : Bravo:
{
DateTime creation;
public override void M()
{
Console.WriteLine(creation);
}
public Delta()
{
creation = DateTime.Now;
}
}
OK, so the expected behavior of this program is that when M is called on any Delta, it will print out the time that the instance was created. But the order of events on new Delta() is:
Bravo ctor runs
Bravo ctor calls this.M
M is virtual and this is of runtime type Delta so Delta.M runs
Delta.M prints out the uninitialized field, which is set to the default time, not the current time.
M returns
Bravo ctor returns
Delta ctor sets the field
Now do you see what I mean when I say that the overriding method might rely on state that is not initialized yet? In any other usage of M, this would be fine because the Delta ctor would already be finished. But here M is called before the Delta ctor even starts!
You create an instance of an object B. It uses the code of the constructor that is defined on class A as you did not override it in B. But the instance is still B, so other methods called in the constructor are the ones defined in B, not A. Hence you see the result of Display() defined in class B.
Update based on update of the question
I'll try to explain the "weird" results you're getting.
When overriding:
B a = new B(); // ---> BB // I expect AB.
A a = new B(); // ---> BB // I expect AB.
This is covered above. When you override a method on a child class, this method is used if you're using an instance of the child class. This is a basic rule that the methods used are decided by the class of the instance of a variable, not by the class used to declare the variable.
When using new modifier for the method (hiding inherited method)
B a = new B(); // ---> AB // I Expect AA here.
Now there's two different behaviours here:
When the constructor is used, it's using the constructor in class A. As the inherited method is hidden in the child class, the constructor is using Display() method from class A and hence you see A printed.
When you later call Display() directly, the instance of the variable is B. For that reason, it uses the method defined on class B which prints B.
Initial statement
I'll start with the base code, I have adapted it to run in LINQPad (I did also change it to Write instead of WriteLine because I'll not preserve the new lines in the explanation anyway).
class A
{
public A()
{
this.Display();
}
public virtual void Display()
{
Console.Write("A"); //changed to Write
}
}
class B :A
{
public override void Display()
{
Console.Write("B"); //changed to Write
}
}
static void Main()
{
A a = new B();
a.Display();
}
The output is:
BB
In your initial question you said you were expecting:
AB
Whats happening here (as Szymon attempted to explain) is that you are creating an object of type B and the class B overrides the method Display of the class A. So whenever you call Display on that object it will be the method of the derived class (B), even from the constructor of A.
I will go over all the cases you mention. I want to encourage to read it carefully. Also, be open minded because this does not match what happens in certain other languages.
1) On override of Display method in the derived class
This is the case where you are overriding the method, ie:
public override void Display()
{
Console.Write("B"); //changed to Write
}
When you override, for all practical uses the method that will be used is the method of the derived class. Think of override as replace.
Case 1:
A a = new A(); // ---> AA
We are ok, with that.
Case 2:
B a = new B(); // ---> BB // I expect AB.
As mentioned above, calling Display on the object will always be the method on the derived class. So, both calls to Display yield B.
Case 3:
A a = new B(); // ---> BB // I expect AB.
This is a variant of the same confusion. The object is clearly of type B, even if you have it in a variable of type A. Remember that in C# the type is a property of the of the object not of the variable. So, the result is the same as above.
Note: You can still use base.Display() to access the method that was replaced.
2) Using the NEW keyword in the Display method in derived class
This is the case where you are hiding the method, ie:
public new void Display()
{
Console.Write("B"); //changed to Write
}
When you hide the method, it means that the original method is still available. You can think of it as a different method (that happens to have the same name and signature). That is: the derived class is not replacing overriding that method.
Because of that, when you do a (virtual) call to the object where at compile time it was decided it was going to use the method of the base class... the method of the derived class is not taken into consideration (in practice, it acts as it weren't a virtual call).
Think of it like this: if you call the method using a varible of the base class... the code is not aware that there exists a derived class that hides the method and that that particular call may be executed with one of those object. Instead, it will use the method of the base class, regardless.
Case 1:
B a = new B(); // ---> AB // I Expect AA here.
You see, at compile time the call in the constructor was set to use the method of the base class. That one gives A. But since the variable is of type B the compiler is aware that the method was hidden for the second call.
Case 2:
A a = new B(); // ---> AA
Here, neither in the constructor nor in the second call it will use the new method. It is not aware of it.
Case 3:
A a = new A(); // ---> AA
And I think this one is clear.
3) Using base.Display()
This the variant of the code where you do this:
public new void Display()
{
base.Display();
Console.Write("B"); //changed to Write
}
base.Display() is gonna be the method in the base class (A), no matter what.
Further reading
You said you want to learn how this works internally.
You can go deeper by reading Microsoft's C# Spec on Virtual Methods
Then read Eric Lippert's Implementing the virtual method pattern in C# (part 1, part 2 and part 3)
You may also be interested:
Anders on virtual calls from constructors.
Eric Lippert's Why Do Initializers Run In The Opposite Order As Constructors? (part 1 and part 2)
Others explanations of Virtual Methods from the web:
What's the difference between override and new? (msdn.com)
C# Virtual (dotnetpearls.com)
Understanding virtual, override and new keyword in C# (dotnet-tricks.com)
Understanding C#: Using virtual and override (oreilly.com)
You may be confusing yourself by naming the class a while instantiating it as class B. If you are looking to call the virtual method you could use the base keyword. The following code writes A B
class A
{
public A()
{
//this.Display();
}
public virtual void Display()
{
Console.WriteLine("A");
}
}
class B : A
{
public override void Display()
{
base.Display();
Console.WriteLine("B");
}
}
class C
{
static void Main(string[] args)
{
A a = new B();
a.Display();
Console.WriteLine();
Console.ReadLine();
}
}
Also note you can see why your code is displaying B B by setting a breakpoint at the beginning and then walking through your code execution line-by-line (F11).
What I understood is , in case of virtual method, same method slot is shared among the parent and child object.
If so, then I think when an object virtual method is called , by somehow compiler updates the method slot with appropriate method address so that exact method is jitted and executed In c#.
class Program
{
static void Main(string[] args)
{
B foo = new B();
foo.DoWork();
Console.ReadLine();
}
}
public class A
{
public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
public override void DoWork() { base.DoWork(); Console.WriteLine("B"); }
}
Why don't I get StackOverflow exception? As I understand it, foo.DoWork() is called, then it calls base.DoWork(), which is virtual and overriden in class B.DoWork() method, which would repeat calling base.DoWork() again until stack is overflown. This overflow is easily achieved when using this instead of base (circular loop of calling self). What does prevent virtual function overriding in this case?
No, when you use base it doesn't make a virtual call. The whole point is to be able to call the base implementation even if you've overridden it.
If you look in the generated IL, you'll see it doesn't use callvirt:
IL_0002: call instance void A::DoWork()
From section 7.6.8 of the C# 5 specification (emphasis mine):
When a base-access references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time (§7.5.4) is changed. The function member that is invoked is determined by finding the most derived implementation (§10.6.3) of the function member with respect to B (instead of with respect to the run-time type of this, as would be usual in a non-base access). Thus, within an override of a virtual function member, a base-access can be used to invoke the inherited implementation of the function member. If the function member referenced by a base-access is abstract, a binding-time error occurs.
A.DoWork is virtual. But a method named by base. is never virtual. That syntax generates a non-virtual call, so the exact method is called, not the most derived version.
A virtual method is just a method that can be overriden and that contains code. When you call base.DoWork() you explicitly state that you want to call A.DoWork(). Then, A.DoWork() is called.
Try making A.DoWork() abstract then it can't contain code. Then, you will have an compilation error on base.DoWork() because there is nothing to execute in base.DoWork().
I am a bit confused about the virtual/new/override thing. Here's an example:
class A
{
public virtual void mVVirtual() { Console.WriteLine("A::mVVirtual"); }
}
class B : A
{
public virtual void mVVirtual() { Console.WriteLine("B::mVVirtual"); }
}
class C : B
{
public override void mVVirtual() { Console.WriteLine("C::mVVirtual"); }
}
class Test
{
static void Main()
{
B b1 = new C();
b1.mVVirtual(); //C::mVVirtual ... I understand this
A a2 = new C();
a2.mVVirtual(); //A::mVVirtual ... ???
}
}
I don't get why in the second call we get A::mVVirtual. I usually treat these issues with this "algorithm":
Check the type of the variable holding the reference for the object for an instance method called mVVirtual? Doesn't have one...but does have a virtual method with that signature and name!
Virtual method? Let's then check the type of the object being held by a2 (C) for an overriding of that method. It has one -> Executes C::mVVirtual!
Where is my "algorithm" wrong? I really am confused by this, and would greatly appreciate some help.
Here's how you think of virtual methods. Every instance of a class has "boxes" to hold methods. When you mark a method as virtual it says make a new "box" and put a method in it. When you mark a method as override in a derived class, it keeps the "box" from the base class but puts a new method in it.
So here you have a class A and a method named mVVirtual that is marked as virtual. This says make a new "box" named mVVirtual and put a method in it with definition
Console.WriteLine("A::mVVirtual");
Then you have a derived class B and a method named mVVirtual that is marked as virtual. This says make a new "box" named mVVirtual and put a method in it with definition
Console.WriteLine("B::mVVirtual");
In particular, the "box" inherited from A is hidden! It can not be seen by objects that are typed as Bs or classes that derive from B.
Then you have a derived class C and a method named mVVirtual that is marked as override. This says take the "box" named mVVirtual inherited from B and put a different method in it with definition
Console.WriteLine("C::mVVirtual");
Now, when you have
B b1 = new C();
b1.mVVirtual();
you are telling the compiler that b1 is a B so that b1.mVVirtual() looks in the "box" mVVirtual and finds the method with definition
Console.WriteLine("C::mVVirtual");
because b1 is really a C and that is what is in the "box" mVVirtual for instances of C.
But when you have
A a2 = new C();
a2.mVVirtual();
you are telling the compiler that a2 is an A and so it looks in the "box" and finds
Console.WriteLine("A::mVVirtual");
The compiler can not know that a2 is really a C (you've typed it as an A) so it does not know that a2 is really an instance of a class that is derived from a class that has hidden the "box" mVVirtual defined by A. What it does know is that A has a "box" named mVVirtual and so it emits code to invoke the method in that "box".
So, to try to put this succinctly:
class A {
public virtual void mVVirtual() { Console.WriteLine("A::mVVirtual"); }
}
defines a class that has a "box" with full name A::mVVirtual but that you can refer to by the name mVVirtual.
class B : A
{
// "new" method; compiler will tell you that this should be marked "new" for clarity.
public virtual void mVVirtual() { Console.WriteLine("B::mVVirtual"); }
}
defines a class that has a "box" with full name B::mVVirtual but that you can refer to by the name mVVirtual. Referring to B.mVVirtual will not refer to the "box" with full name A::mVVirtual; that "box" can not be seen by objects that are typed as Bs (or classes that derive from B).
class C : B
{
public override void mVVirtual() { Console.WriteLine("C::mVVirtual"); }
}
defines a class that takes the "box" with full name B::mVVirtual and puts a different method in it.
Then
A a2 = new C();
a2.mVVirtual();
says that a2 is an A so that a2.mVVirtual looks in the "box" with full name A::mVVirtual and invokes the method in that "box". This is why you see
A::mVVirtual
on the console.
There are two other method annotaters. abstract makes a new "box" does not put a method definition in the "box". new makes a new "box" and puts a method definition in the "box" but does not allow derived classes to put their own definitions of the method in the "box" (use virtual if you want to do that).
Sorry for being long-winded but I hope that helps.
UPDATE : For a more information about this language feature, see the follow-up question here: More about Virtual / new...plus interfaces!
Jason's answer is correct. To sum it up a bit more succinctly.
You have three methods. Call them MA, MB and MC.
You have two "boxes", or, as they're usually called, slots. We'll stick with Jason's nomenclature. Call them BOX1 and BOX2.
"A" defines BOX1.
"B" defines BOX2.
"C" defines no box; it reuses BOX2.
When you say "new A()", BOX1 is filled in with MA.
When you say "new B()", BOX1 is filled in with MA and BOX2 is filled in with MB.
When you say "new C()", BOX1 is filled in with MA and BOX2 is filled in with MC.
Now suppose you have a variable of type A, and a call to the method. Reason like the compiler. The compiler says "are there any boxes on type A that match this name?" Yes, there is one: BOX1. Therefore, the compiler generates a call to the contents of BOX1.
As we've seen, the contents of BOX1 is always MA, so MA is always called no matter whether the variable is actually holding a reference to A, B, or C.
Now suppose you have a variable of type B, and a call to the method. Again, think like the compiler. The compiler says "are there any boxes on type B that matches this name?" Yes, there are TWO boxes that match by name. The compiler says "which of those two is more closely associated with B?" The answer is BOX2, because B declares BOX2. Therefore, the compiler generates a call to BOX2.
This will call MB if the variable contains a B, because in a B, BOX2 contains MB. This will call MC if the variable contains a C, because in a C, BOX2 contains MC.
Is that now clear? Remember, overload resolution just chooses the box. What the contents of the box are depend upon the object at runtime.
Do you have warnings hidden? When I do what you've done, I get this warning:
'ProjectName.ClassName.B.mVVirtual()' hides inherited member 'ProjectName.ClassName.A.mVVirtual()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
If you used override in class B, you wouldn't have this problem; both cases would give you "C::mVVirtual". Since you're not using override in class B, there's an implicit new in front of the method. This breaks the inheritance chain. Your code is calling a method on type A, and there are no inheriting classes that override that method due to the implicit new. So it has to call class A's implementation.
Best way to think of it is that virtual methods use the actual (or concrete) type of the object to decide what implementation to execute, where non-Virtual methods use the 'declared type of the variabe you are using to access the method to decide which to run...
Override means you are writing a method that is going to 'replace' the implementation for a virtual or abstract method (with the same name/signature) higher up the inheritance chain.
new is used when there is a non-virtual method up the chain with the same name/signature, which the method you are adding will replace...
The difference is as follows
class base { public virtual void foo() { Console.write("base.foo"); } }
class derived { public override void foo() { Console.write("derived.foo"); } }
base b = new base();
b.foo() // prints "base.foo" // no issue b is a base and variable is a base
base b = new derived();
b.foo(); // prints "derived.foo" because concrete tyoe is derived, not base
but
class base { public void foo() { Console.write("base.foo"); } }
class derived { public new void foo() { Console.write("derived.foo"); } }
base b = new base();
b.foo() // prints "base.foo" // no issue b is a base and variable is a base
base b = new derived();
b.foo(); // prints "base.foo" because variable b is base.
derived d = b as derived;
d.foo() // prints "derived.foo" - now variable d is declared as derived.
Your second call prints A::mvVirtual because that method (mVVirtual) is actually not virtual, (in spite of it's name), because it has the new specifier... So it decides based on the variable type, which is A.
To explain what is going on technically, Every Type has a "method Table" with pointers to all the methods in that type. (It is NOT the instance of a type that has this table, It is the TYPE itself.) Each Types' method table is structured with all acessible virtual methods first, from object (furthest up the inheritance chain) at the beginning, to the virtual methods declared in the type itself, at the end. Then, after all the virtual methods are represented, all the non-virtual methods are added, again, from any non-virtual methods in object, first, all the way to any non-virtual methods in the Type itself. The table is structured this way so that the offsets for all virtual metods will be identical in all derived classes' method tables, since the compiler may call these methods from variables declared as other types, even from code in other methods declared and implemented in base types of the concrete class.
When the compiler resolves a virtual method call it goes to the method table for the Type of the object itself, (the concrete type), whereas for a non-virtual call it goes to the method table for declared type of the variable. So if you call a virtual method, even from code in a base type, if the actual concrete type is Type derived from this base type, the compiler goes to the method table for that concrete type.
If you call a non-virtual method, (no matter how far down the inheritance change the actual object's type might be), The compiler access the method table for the variab;es' declared type. This table has nothing in from any derived types furthur down the chain.
This is how I understand it
A is the base class
B inherit A but doesn't override it
C inherit B but does override it
Since you are declaring A but initialize C, it will ignore the override because the base class is A and A never get overridden from B.