Menu
Coddy logo textTech

Virtual & Override Keywords

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 21 of 70.

When a derived class inherits a method, sometimes you want to completely change how that method behaves. The virtual and override keywords work together to make this possible in a controlled way.

Mark a method as virtual in the base class to signal that derived classes are allowed to replace its implementation:

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

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

The override keyword in the derived class tells the compiler you're intentionally replacing the base method. Without virtual on the base method, you cannot use override.

The real power shows when you use a base class reference to hold a derived object:

Animal myPet = new Dog();
myPet.Speak();  // Output: Woof!

Even though myPet is declared as Animal, C# calls the Dog version of Speak(). This is called runtime polymorphism - the actual method called depends on the object's true type, not the variable's declared type.

You can still access the base implementation using base.Speak() inside your override if you want to extend rather than completely replace the behavior.

challenge icon

Challenge

Easy

Let's build a notification system that demonstrates how derived classes can completely override base class behavior using virtual and override keywords, while also showing the power of runtime polymorphism.

You'll create three files to organize your code:

  • Notification.cs: Define a Notification class in the Alerts namespace. This base class represents a generic notification with a Message property (string) and a constructor that sets it. Include a virtual method called Send() that returns "Sending notification: {Message}".
  • EmailNotification.cs: Define an EmailNotification class in the Alerts namespace that inherits from Notification. Add a Recipient property (string) and a constructor that accepts both message and recipient (use base to pass the message). Override the Send() method to return "Emailing {Recipient}: {Message}".
  • SmsNotification.cs: Define an SmsNotification class in the Alerts namespace that also inherits from Notification. Add a PhoneNumber property (string) and a constructor that accepts both message and phone number. Override Send() to return "SMS to {PhoneNumber}: {Message}".
  • Program.cs: In your main file, create instances of all three notification types using input values. Then demonstrate runtime polymorphism by storing all three in Notification variables and calling Send() on each one.

You will receive four inputs:

  • The notification message
  • Email recipient address
  • Phone number for SMS

Print the result of calling Send() on each notification, stored as base class references:

{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}

For example, if the inputs are Meeting at 3pm, alice@email.com, and 555-1234, the output should be:

Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pm

Notice how even though all three are stored as Notification variables, each one calls its own overridden version of Send(). That's runtime polymorphism in action!

Cheat sheet

Use the virtual keyword in a base class to mark a method that derived classes can override:

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

Use the override keyword in a derived class to replace the base class method implementation:

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

Runtime polymorphism occurs when a base class reference holds a derived object - the actual method called depends on the object's true type, not the variable's declared type:

Animal myPet = new Dog();
myPet.Speak();  // Output: Woof!

You can access the base implementation inside an override using base:

public override void Speak()
{
    base.Speak();  // Calls the base class version
    Console.WriteLine("Woof!");
}

Try it yourself

using System;
using Alerts;

class Program
{
    public static void Main(string[] args)
    {
        // Read inputs
        string message = Console.ReadLine();
        string email = Console.ReadLine();
        string phone = Console.ReadLine();

        // TODO: Create instances of all three notification types
        // Store them in Notification variables to demonstrate polymorphism
        
        // TODO: Create a base Notification
        Notification baseNotification;
        
        // TODO: Create an EmailNotification (stored as Notification type)
        Notification emailNotification;
        
        // TODO: Create an SmsNotification (stored as Notification type)
        Notification smsNotification;
        
        // TODO: Call Send() on each and print the results
        // The overridden methods will be called due to runtime polymorphism
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming