Why does calling a method in my derived class call the base class method?

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:

Illustration of method implementations

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:

Illustration of abstract classes

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, Students and Teachers are Persons, 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 Students ShowInfo:

Illustration of inherited methods

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

Illustration of a virtual member inside a base class

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

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:

Illustration of the default implementation of a method, using virtual-keyword

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:

Illustration of the "way around" using the new-keyword

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!

Leave a Comment