Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion about virtual/new/override

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":

  1. 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!
  2. 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.

like image 875
Bruno Avatar asked Jan 14 '10 16:01

Bruno


1 Answers

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.

like image 99
jason Avatar answered Oct 31 '22 13:10

jason