Method Overriding - Customizing Inherited Behavior in C#

Vaibhav • September 10, 2025

In the previous article, we explored base and derived classes - how inheritance allows you to reuse and extend behavior across related types. Now, we focus on a powerful feature that makes inheritance truly flexible: method overriding. This article explains how derived classes can redefine methods from their base class to provide specialized behavior, and how C# supports this through the virtual and override keywords.

Method overriding is essential for implementing polymorphism - the ability to treat objects of different types through a common interface while allowing each type to behave differently. In this article, we’ll explore how overriding works, how to use it correctly, and how to avoid common mistakes when customizing inherited behavior.

What is Method Overriding?

Method overriding allows a derived class to provide its own implementation of a method that is defined in its base class. This is useful when the base class defines general behavior, and the derived class needs to specialize or replace that behavior.

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

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Dog says woof");
    }
}

In this example, Speak() is defined in the base class Animal and marked as virtual. The derived class Dog overrides it using the override keyword to provide a dog-specific implementation.

Animal a = new Dog();
a.Speak(); // Output: Dog says woof

Even though a is declared as Animal, the overridden method in Dog is called. This is polymorphism in action.

To override a method, the base method must be marked virtual, abstract, or override. The derived method must use the override keyword.

Using virtual and override

The virtual keyword in the base class signals that a method can be overridden. The override keyword in the derived class replaces the base implementation.

public class Vehicle
{
    public virtual void Start()
    {
        Console.WriteLine("Vehicle starting");
    }
}

public class Car : Vehicle
{
    public override void Start()
    {
        Console.WriteLine("Car engine starting");
    }
}

This pattern allows derived classes to customize behavior while maintaining a consistent interface.

Calling the Base Method

Sometimes you want to extend the base method rather than replace it entirely. You can use the base keyword to call the base implementation from the overridden method.

public class Bird : Animal
{
    public override void Speak()
    {
        base.Speak(); // Calls Animal.Speak()
        Console.WriteLine("Bird chirps");
    }
}

This allows you to build on the base behavior while adding new functionality.

Overriding Properties

You can also override properties in derived classes. This is useful when the property logic needs to change.

public class Shape
{
    public virtual double Area => 0;
}

public class Circle : Shape
{
    public double Radius { get; set; }

    public override double Area => Math.PI * Radius * Radius;
}

The Area property is overridden in Circle to provide a meaningful calculation. This supports polymorphic access to properties.

Overriding ToString()

One of the most commonly overridden methods is ToString(), which returns a string representation of an object. The default implementation returns the type name, but you can override it to provide more useful output.

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public override string ToString()
    {
        return $"{Name} - ${Price}";
    }
}

Now, printing a Product object gives a readable summary:

Product p = new Product { Name = "Laptop", Price = 999.99m };
Console.WriteLine(p); // Output: Laptop - $999.99

Preventing Overriding with sealed

If you want to prevent further overriding of a method, you can mark it as sealed. This is useful when you want to lock down behavior in a derived class.

public class Printer
{
    public virtual void Print() => Console.WriteLine("Printing...");
}

public class SecurePrinter : Printer
{
    public sealed override void Print() => Console.WriteLine("Secure print");
}

public class AdvancedPrinter : SecurePrinter
{
    // ❌ Cannot override Print() here
}

The Print() method in SecurePrinter is sealed, so AdvancedPrinter cannot override it. This helps enforce design constraints.

Abstract Methods and Overriding

An abstract method has no implementation in the base class and must be overridden in derived classes. This is useful when the base class defines a contract but leaves the implementation to subclasses.

public abstract class Shape
{
    public abstract double Area();
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public override double Area() => Width * Height;
}

The Shape class defines an abstract method Area(). Each derived class must provide its own implementation.

Abstract methods can only exist in abstract classes. You cannot instantiate an abstract class directly.

Common Mistakes to Avoid

Method overriding is powerful, but it can lead to bugs if misused. Avoid these common mistakes:

  • Forgetting to mark the base method as virtual.
  • Using new instead of override (covered in the next article).
  • Overriding methods without understanding base behavior.
  • Breaking polymorphism by calling methods directly on derived types.

Always design base methods with overriding in mind. Document their purpose and expected behavior clearly.

Summary

Method overriding allows derived classes to customize behavior defined in base classes. It supports polymorphism, promotes extensibility, and helps model real-world variations in behavior. In C#, overriding is enabled through the virtual and override keywords, and can be applied to methods, properties, and indexers.

We explored how to override methods, how to call base implementations, how to use abstract and sealed modifiers, and how to avoid common mistakes. We also saw how overriding enables polymorphic behavior and improves code reuse.

As you continue building class hierarchies, use method overriding to specialize behavior while maintaining a consistent interface. In the next article, we’ll explore Method Hiding - how to redefine base methods without overriding them, and when to use the new keyword.