1
class Program
{
    static void Main(string[] args)
    {
        Parent p = new Child();
        p.Print();
    }
}
class Parent
{
    public virtual void Print()
    {
        Console.WriteLine("This is parent.");
    }
}
class Kid:Parent
{
    public override void Print()
    {
        Console.WriteLine("This is Kid.");
    }
}
class Child : Kid
{
    public new virtual void Print()
    {
        Console.WriteLine("This is Child.");
    }
}  

Why is the output "This is Kid", and not "This is child"?

The Print() in class Child is virtual.
I'm trying to understand what is happening.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • Your question (What do they do) does not match the title (How does it work). But both are (many time) duplicates. And you're unclear about _what_ you don't understand. – H H Aug 14 '12 at 10:14
  • @user1559463 I've edited your question title. Now fits better with your topic. – Matías Fidemraizer Aug 14 '12 at 10:26

5 Answers5

1

You are calling overridden Print method of Parent.

new operator will work if you type that variable as Kid. In this case, new isn't an override but an identifier reuse.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
1

p is of type Parent. So the compiler looks for a Print method in the Parent class. As this method is virtual, it finds an overridden method in the Kid class. Because you didn't override but replace the Print method in Child, the compiler doesn't use this method.

fero
  • 6,050
  • 1
  • 33
  • 56
  • The compiler sees the type is "Parent", and the Pointed type is "Child"? then he finds the virtual method, and sees that the Pointer is pointing to a different type, so his looking for a method that overrided the current method? if he doesn't find, he will use the method in "Parent"? – user1559463 Aug 14 '12 at 10:20
  • @user1559463 The compiler sees that the type of the variable is `Parent` and therefore tries to invoke the method `Print` of type `Parent`. If your method in wasn't virtual, your program would display "This is parent". But as it is virtual the compiler looks for overriding methods and calls the most specific override - which is in `Kid`. That's because your "override-chain" stops in `Kid` because you used the `new` keyword for your `Child.Print()` method. – fero Aug 14 '12 at 11:06
  • @user1559463 If you want to display "This is Child" you have to replace `new virtual` with another `override` to tell the compiler that this method is a more specific version of the inherited method from `Kid` or `Parent`, and not a completely new one. – fero Aug 14 '12 at 11:14
0

Well because you used the new keyword it means that you hid the inherited method and provided a new implementation. This is commonly referred to as hiding parent members.

Freeman
  • 5,691
  • 3
  • 29
  • 41
0

From MSDN ( http://msdn.microsoft.com/en-us/library/6fawty39(v=vs.100).aspx ):

"When DoWork is called on an instance of Derived, the C# compiler will first try to make the call compatible with the versions of DoWork declared originally on Derived. Override methods are not considered as declared on a class, they are new implementations of a method declared on a base class. Only if the C# compiler cannot match the method call to an original method on Derived will it try to match the call to an overridden method with the same name and compatible parameters."

Looks like your use of 'new' in Kid but declaring p as Parent means that p cannot see the Print in Child as it's not part of the inheritance hierarchy.

static void Main(string[] args)
{
    Parent p = new Child();
    p.Print();

    Child c = (Child) p;
    c.Print();
}

...obviously changes things.

David Osborne
  • 6,436
  • 1
  • 21
  • 35
0

The following both over-simplifies and treats one possible implementation as fact, but should suffice to have a working mental model.

When calling code "knows about" a class it knows the following things:

  1. Fields can be accessed at a particular offset from the object's location. So for example if an object is at address 120, and has two integer fields, then it might be able to access one them at address 124. If another object of the same type was at address 140, the equivalent field would be at 144.

  2. Non-virtual methods (and properties can be considered syntactic sugar on one or two methods) are functions at a particular address that take a reference to the object you are calling on (this from within the method) and the other parameters of that function.

  3. Virtual methods are like the above, but their address can be found by looking at a particular offset within a table associated with the class, the address of which will also be a particular offset from the address of the class.

In this, Kid has a table of methods that is a superset of that of Parent (it could add more methods), and which has the same function address for those methods it didn't over-ride (calling Equals on it uses the same function as calling Equals on a Parent), but a different address for those it does over-ride (Print() in this case).

Hence if you have a Kid then whether you have it through a Parent reference or a Kid reference, calling Print() will look to the same table, look up the location of the Print() method, and call it.

In the case of Child, there is new used on the Print method. This tells the compiler that we specifically want a different table. Hence, if we call Print() via a Child reference, it looks up the Child-specific table, and calls the method it finds. If however we call it via a Kid or Parent reference, then we don't even know that there is a Child-specific table we could be using and we look up the function in the table we know Kid and Parent have respectively, and call the function found (that defined in Kid).

As a rule, new is to be avoided. It's use is in two places:

One is backwards compatibility. If for example Child had a Name property, and then later on the code for Parent was changed so that it too had a Name property, we've a conflict. Since Child's Name isn't an override, it gets treated as if it had new but gives us a warning, as this is the only way code using the old way of things and code that knows about the new Name on Parent can co-exist. If we ever come back to re-compile Child we should probably either refactor so it doesn't have its own Name (if that on Parent does what we want), refactor so it is an override, refactor to something completely different, or add new to indicate that this is how we want things to be despite it being less than ideal.

The other is when new allows for a more specific form of the same behaviour that the base class' method allows, but is logically compatible (so users don't get surprised). This latter should go in the semi-advanced techniques box and not be done lightly. It should also be commented as such, because most of the time seeing new means your dealing with something that is at best a compromise and should probably be improved.

(Aside: am I the only person who thought of tabloid newspapers upon seeing Kids having Children?)

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251