Why does calling a method in my derived class call the base class method?
Consider this code:
class Program
{
static void Main(string[] args)
{
Person person = new Teacher();
person.ShowInfo();
Console.ReadLine();
}
}
public class Person
{
public void ShowInfo()
{
Console.WriteLine("I am Person");
}
}
public class Teacher : Person
{
public new void ShowInfo()
{
Console.WriteLine("I am Teacher");
}
}
When I run this code, the following is outputted:
I am Person
However, you can see that it is an instance of Teacher
, not of Person
. Why does the code do that?
There's a difference between new
and virtual
/override
.
You can imagine, that a class, when instantiated, is nothing more than a table of pointers, pointing to the actual implementation of its methods. The following image should visualize this pretty well:
Now there are different ways, a method can be defined. Each behaves different when it is used with inheritance. The standard way always works like the image above illustrates. If you want to change this behavior, you can attach different keywords to your method.
1. Abstract classes
The first one is abstract
. abstract
methods simply point to nowhere:
If your class contains abstract members, it also needs to be marked as abstract
, otherwise the compiler will not compile your application. You cannot create instances of abstract
classes, but you can inherit from them and create instances of your inherited classes and access them using the base class definition. In your example this would look like:
public abstract class Person
{
public abstract void ShowInfo();
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
public class Student : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a student!");
}
}
If called, the behavior of ShowInfo
varies, based on the implementation:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a student!'
Both, Student
s and Teacher
s are Person
s, but they behave different when they are asked to prompt information about themselves. However, the way to ask them to prompt their information, is the same: Using the Person
class interface.
So what happens behind the scenes, when you inherit from Person
? When implementing ShowInfo
, the pointer is not pointing to nowhere any longer, it now points to the actual implementation! When creating a Student
instance, it points to Student
s ShowInfo
:
2. Virtual methods
The second way is to use virtual
methods. The behavior is the same, except you are providing an optional default implementation in your base class. Classes with virtual
members can be instanciated, however inherited classes can provide different implementations. Here's what your code should actually look like to work:
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am a person!");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
The key difference is, that the base member Person.ShowInfo
isn't pointing to nowhere any longer. This is also the reason, why you can create instances of Person
(and thus it does not need to be marked as abstract
any longer):
You should notice, that this doesn't look different from the first image for now. This is because the virtual
method is pointing to an implementation "the standard way". Using virtual
, you can tell Persons
, that they can (not must) provide a different implementation for ShowInfo
. If you provide a different implementation (using override
), like I did for the Teacher
above, the image would look the same as for abstract
. Imagine, we did not provide a custom implementation for Student
s:
public class Student : Person
{
}
The code would be called like this:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a person!'
And the image for Student
would look like this:
3. The magic `new` keyword aka "Shadowing"
new
is more a hack around this. You can provide methods in generalized classes, that have the same names as methods in the base class/interface. Both point to their own, custom implementation:
The implementation looks like the one, you provided. The behavior differs, based on the way you access the method:
Teacher teacher = new Teacher();
Person person = (Person)teacher;
teacher.ShowInfo(); // Prints 'I am a teacher!'
person.ShowInfo(); // Prints 'I am a person!'
This behavior can be wanted, but in your case it is misleading.
I hope this makes things clearer to understand for you!
Subtype polymorphism in C# uses explicit virtuality, similar to C++ but unlike Java. This means that you explicitly have to mark methods as overridable (i.e. virtual
). In C# you also have to explicitly mark overriding methods as overriding (i.e. override
) to prevent typos.
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am Person");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am Teacher");
}
}
In the code in your question, you use new
, which does shadowing instead of overriding. Shadowing merely affects the compile-time semantics rather than the runtime semantics, hence the unintended output.
You have to make the method virtual and you have to override the function in the child class, in order to call the method of class object you put in parent class reference.
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am Person");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am Teacher");
}
}
Virtual Methods
When a virtual method is invoked, the run-time type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member. By default, methods are non-virtual. You cannot override a non-virtual method. You cannot use the virtual modifier with the static, abstract, private or override modifiers, MSDN.
Using New for Shadowing
You are using new key word instead of override, this is what new does
If the method in the derived class is not preceded by new or override keywords, the compiler will issue a warning and the method will behave as if the new keyword were present.
If the method in the derived class is preceded with the new keyword, the method is defined as being independent of the method in the base class, This MSDN article explains it very well.
Early binding VS Late binding
We have early binding at compile time for normal method (not virtual) which is the currrent case the compiler will bind call to method of base class that is method of reference type (base class) instead of the object is held in the referece of base class i.e. derived class object. This is because ShowInfo
is not a virtual method. The late binding is performed at runtime for (virtual / overridden method) using virtual method table (vtable).
For a normal function, the compiler can work out the numeric location of it in memory. Then it when the function is called it can generate an instruction to call the function at this address.
For an object that has any virtual methods, the compiler will generate a v-table. This is essentially an array that contains the addresses of the virtual methods. Every object that has a virtual method will contain a hidden member generated by the compiler that is the address of the v-table. When a virtual function is called, the compiler will work out what the position is of the appropriate method in the v-table. It will then generate code to look in the objects v-table and call the virtual method at this position, Reference.