DEV Community

Gokul
Gokul

Posted on

C# Inheritance: Virtual, Override, New, and Hidden Dangers

Image description
C# inheritance is quite easy to implement but sometimes complex to understand.

Sure, most developers know what virtual, override, and new do. But what if we throw in a couple of casts and a few extra levels of inheritance? Now things aren’t so straightforward.

This article assumes you already know what inheritance is.


Initial Setup:

internal class Animal
{
    public virtual void sound()
    {
        Console.WriteLine("Animal makes noise");
    }

    public virtual void move()
    {
        Console.WriteLine("Animal moved");
    }
}

internal class Dog : Animal
{
    public override void sound()
    {
        Console.WriteLine("Dog barks");
    }

    public virtual new void move()
    {
        Console.WriteLine("Dog runs");
    }
}

internal class SuperDog : Dog
{
    public override void sound()
    {
        Console.WriteLine("Super Dog howls");
    }

    public override void move()
    {
        Console.WriteLine("Super Dog sprints");
    }
}

internal class Duck : Animal
{
    public new void sound()
    {
        Console.WriteLine("Duck quacks");
    }

    public override void move()
    {
        Console.WriteLine("Duck Duck go!");
    }
}
Enter fullscreen mode Exit fullscreen mode

❓ Question 1: What is the output?

Animal al = new Animal();
al.sound(); 
al.move();  

Animal d1 = new Dog();
d1.sound(); 
d1.move();  

Animal d2 = new SuperDog();
d2.sound(); 
d2.move();  

Dog sd = new SuperDog();
sd.sound(); 
sd.move(); 

Animal dk = new Duck();
dk.sound(); 
dk.move();  
Enter fullscreen mode Exit fullscreen mode

❓ Question 2: What does virtual new mean?

  • Does it still hide the base implementation?
  • Does it still declare the method for further override?

❓ Question 3: Guess the output!

Update the Dog class like this:

internal class Dog : Animal
{
    public override void sound()
    {
        base.sound();
        Console.WriteLine("Dog barks");
    }

    public virtual new void move()
    {
        base.move();
        Console.WriteLine("Dog runs");
    }
}
Enter fullscreen mode Exit fullscreen mode

Now what do you think this will print?

Dog d3 = new Dog();
d3.sound(); 
d3.move();  
Enter fullscreen mode Exit fullscreen mode

Constructors and Virtual Methods

Constructors are meant to initialize the class state before it can be used.

public class Base
{
    public Base()
    {
        Console.WriteLine("Base constructor");
        Init(); 
    }

    public virtual void Init()
    {
        Console.WriteLine("Init Called: Base class");
        // Base logic (maybe logging or setup)
    }
}

public class Derived : Base
{
    private List<string> _logMessages;
    private string _message = "Initialization starts";

    public Derived()
    {
        _logMessages = new List<string>(); 
    }

    public override void Init()
    {
        Console.WriteLine(_message);
        _logMessages.Add("Init Called: Derived class"); 
    }
}
Enter fullscreen mode Exit fullscreen mode

❓ Question 4: Answer the following:

  1. Does the above code run properly?
  2. What does the output look like?

✅ Solutions:

Question 1:

Animal al = new Animal();
al.sound(); // Animal makes noise
al.move();  // Animal moved

Animal d1 = new Dog();
d1.sound(); // Dog barks
d1.move();  // Animal moved

Animal d2 = new SuperDog();
d2.sound(); // Super Dog howls
d2.move();  // Animal moved

Dog sd = new SuperDog();
sd.sound(); // Super Dog howls
sd.move();  // Super Dog sprints

Animal dk = new Duck();
dk.sound(); // Animal makes noise
dk.move();  // Duck Duck go!
Enter fullscreen mode Exit fullscreen mode

Question 2:

virtual new declares a method that:

  • Hides the current base class implementation, and
  • Allows the method to be overridden in future derived classes.

So yes, it hides the base method for the current class and opens it for further overriding.


Question 3:

If a method exists in the base class and is accessible (not private), base.MethodName() will work inside the derived class—whether it's overridden or hidden using new.

Output:

Animal makes noise
Dog barks
Animal moved
Dog runs
Enter fullscreen mode Exit fullscreen mode

Question 4:

Does the above code run normally?

Hell no!

Why?

Assume we create an object like this:

Derived dd = new Derived();
Enter fullscreen mode Exit fullscreen mode

What really happens:

  1. CLR starts memory allocation for the object.
  2. Field _message is declared and initialized.
  3. Field _logMessages is declared, but not yet initialized.
  4. The base class constructor is invoked (derived constructor hasn't run yet).
  5. Inside the base constructor, Init() is called.
  6. Because it's virtual, it dispatches to Derived.Init().
  7. In Derived.Init(), _logMessages.Add(...) is called—but _logMessages is null!
  8. 💥 NullReferenceException

Output before crash:

Base constructor
Initialization starts
Unhandled Exception: System.NullReferenceException
Enter fullscreen mode Exit fullscreen mode

💬 Final Thought:

While this scenario may not be common in enterprise apps, it can easily slip in unnoticed—especially when refactoring or extending base functionality. Most exceptions are just mistakes we didn't anticipate.

If you like this article please leave a comment, i am new to blogging but love programming, i am trying to expand my circle and would now to interact with others, Thanks

Top comments (0)