-1
class A {
    public virtual void M() { Console.Write("A"); }
}

class B: A {
    public override void M() { Console.Write("B"); }
}

class C: B {
    new public virtual void M() { Console.Write("C"); }
}

class D: C {
    public override void M() { Console.Write("D"); }
    static void Main() {
        D d = new D(); 
        C c = d; 
        B b = c; 
        A a = b;
        d.M(); c.M(); b.M(); a.M();
    }
}

I'm new to this concept, so a detailed answer would be appreciated.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
Rsaleh
  • 77
  • 1
  • 10
  • The new modifier doesn't get involved for A/B - can you clarify, do you mean why does it not output "B" for C? Really the answer for A/B has nothing to do with the new modifier. The override ensures the most derived method is called. – Charleh May 29 '13 at 22:45
  • possible duplicate of [C# keyword usage virtual+override vs. new](http://stackoverflow.com/questions/159978/c-sharp-keyword-usage-virtualoverride-vs-new) – Anthony Pegram May 30 '13 at 00:12
  • possible duplicate of http://stackoverflow.com/questions/1067234/oh-virtual-new-modifiers-in-c-sharp-have-different-rules-so-what-is-the – Anthony Pegram May 30 '13 at 00:12
  • possible duplicate of http://stackoverflow.com/questions/1014295/new-keyword-in-method-signature (could do this all day) (please search and test your code, then you'll know) – Anthony Pegram May 30 '13 at 00:13
  • @user414076 my question is a result of testing the code. – Rsaleh Jun 10 '13 at 05:25

2 Answers2

5

The new modifier essentially creates a new method of the same name, hiding the base method of the same name; the base method is still there, it's just been hidden. You can still call it from outside the class simply by casting it to the base type. From the documentation:

When used as a modifier, the new keyword explicitly hides a member inherited from a base class.

On the other hand, the override keyword indicates that the base method will be overriden in the sub class. There is no way to call the base method without going through the overriden interface.

So in your example, when you call b.M() and a.M() you're calling the M method on B (which is inheritted and overridden from A). On the other hand when you call d.M() and c.M() you're calling a completely different method named M on D (which is inheritted and overridden from C).

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • b.M() and a.M() are both calling method M on B and d.M() and c.M() are actually both calling M on D. – Kevin May 29 '13 at 22:47
  • @Kevin isn't c is just a reference to object of type D ? why the answer for both b.M() and a.M() isn't "D" ? – Rsaleh May 29 '13 at 23:18
  • @Rsaleh With `new`, you are creating a whole new virtual method that simply happens to have the name of an existing one, but is not an override. – Theodoros Chatzigiannakis May 29 '13 at 23:56
1

When you define a virtual method you are allowing that method to be overridden at some point in the inheritance tree. A descendant class such as B in your example can change the logic of the method in some way. Calls to the virtual method will use the most derived override, dependent on the concrete type of an object instance rather than the type of the variable that holds the instance.

A Virtual Method Table (VMT) is used to track these virtual methods and any overrides that may have been applied. A variable of type A may hold any class that is derived from class A - in your example, any of A, B, C or D. The VMT is used to determine which override method to call when you call a virtual method. So in your example, B overrides method M. When you invoke method M the program looks in the VMT for the correct method to call, and invokes that method.

Each class defines its own VMT, and a variable will use the VMT that relates to its own type. Thus a variable of type A will use the VMT for type A, type B variables will use the VMT for type B and so on, regardless of the actual (concrete) class of the instance stored in that variable. Override resolution will process the VMTs of the instance to find the correct method to execute.

The new operator declares a method that has the same name, but is not part of the resolution chain for the method it replaces. The new method may be virtual, in which case it gets its own set of VMT entries, or it might be a standard non-virtual method.

In order to correctly manage virtual methods and overrides, the compiler must have some information to work with. Since it cannot know at compile-time what a variable might hold, it only has the variable's type to go by. So when you call a method on a variable of type A, it resolves the call as it should be done for type A - by generating code to do run-time overload resolution on an object of type A. Same goes for variables of any type... the generated code works on the declared type, and resolves overrides at run-time.

So when you declare a variable of type C, then invoke method M, it is looking for the method named M that is defined at or below C. Since you used the new modifier, this will never result in back-tracking to the virtual method defined in A or its overload in B. Since C.M is virtual it will use the VMTs to find the right override of your new virtual method.

So in general which method is actually called when a virtual method is invoked is dependent on the concrete type of the instance. The new keyword makes the type of the variable important as well.

Some of the above is more descriptively accurate than technically accurate. The actual implementation may differ, as always :)

Corey
  • 15,524
  • 2
  • 35
  • 68