Skip to main content

C# Method Overriding

Introduction

Method overriding is a fundamental concept in object-oriented programming that enables a derived class to provide a specific implementation of a method that is already defined in its base class. This feature is a key component of polymorphism, one of the four pillars of object-oriented programming.

In C#, method overriding allows a derived class to extend or modify the behavior of methods inherited from a base class, making your code more flexible and maintainable. When a method in a derived class has the same name, return type, and parameters as a method in its base class, it can be declared as an override of the base method.

Prerequisites

Before diving into method overriding, you should be familiar with:

  • Basic C# syntax
  • Classes and objects
  • Inheritance

The Basics of Method Overriding

In C#, method overriding requires two special keywords:

  1. virtual - Applied to the base class method to indicate that it can be overridden
  2. override - Applied to the derived class method that overrides the base class method

Here's a simple example:

csharp
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}

public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("The dog barks: Woof! Woof!");
}
}

public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("The cat meows: Meow!");
}
}

Let's see how we can use these classes:

csharp
static void Main(string[] args)
{
Animal myAnimal = new Animal();
Animal myDog = new Dog();
Animal myCat = new Cat();

myAnimal.MakeSound(); // Output: The animal makes a sound
myDog.MakeSound(); // Output: The dog barks: Woof! Woof!
myCat.MakeSound(); // Output: The cat meows: Meow!

Console.ReadLine();
}

In this example, even though myDog and myCat are declared as Animal types, the program calls the overridden methods in the derived classes. This is the essence of polymorphism - objects behave differently based on their actual type at runtime.

Rules for Method Overriding in C#

  1. The base class method must be declared with the virtual, abstract, or override keyword.
  2. The derived class method must be declared with the override keyword.
  3. Both methods must have the same name, return type, and parameter list.
  4. The access modifier of the overriding method cannot be more restrictive than that of the overridden method.

Accessing the Base Class Implementation

Sometimes you might want to extend the base class method rather than completely replace it. C# allows you to call the base class implementation using the base keyword:

csharp
public class Bird : Animal
{
public override void MakeSound()
{
// Call the base class implementation first
base.MakeSound();
// Then add additional behavior
Console.WriteLine("The bird chirps: Tweet! Tweet!");
}
}

Using this class:

csharp
static void Main(string[] args)
{
Animal myBird = new Bird();
myBird.MakeSound();

Console.ReadLine();
}

Output:

The animal makes a sound
The bird chirps: Tweet! Tweet!

Abstract Methods and Overriding

An abstract method is a method without an implementation that must be overridden in derived classes. Abstract methods can only be declared in abstract classes or interfaces.

csharp
public abstract class Shape
{
public abstract double CalculateArea();

public void Display()
{
Console.WriteLine($"This shape has an area of {CalculateArea()} square units");
}
}

public class Circle : Shape
{
private double radius;

public Circle(double r)
{
radius = r;
}

public override double CalculateArea()
{
return Math.PI * radius * radius;
}
}

public class Rectangle : Shape
{
private double length;
private double width;

public Rectangle(double l, double w)
{
length = l;
width = w;
}

public override double CalculateArea()
{
return length * width;
}
}

Usage:

csharp
static void Main(string[] args)
{
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);

circle.Display(); // Output: This shape has an area of 78.53981633974483 square units
rectangle.Display(); // Output: This shape has an area of 24 square units

Console.ReadLine();
}

Sealed Methods

In some cases, you may want to prevent further overriding of a method. The sealed keyword allows you to seal a method so that derived classes cannot override it:

csharp
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}

public class Dog : Animal
{
public sealed override void MakeSound()
{
Console.WriteLine("The dog barks: Woof! Woof!");
}
}

// This would cause a compile-time error
public class Labrador : Dog
{
// Error: Cannot override sealed member 'Dog.MakeSound()'
public override void MakeSound()
{
Console.WriteLine("The labrador barks loudly: WOOF! WOOF!");
}
}

Real-World Example: Payment Processing System

Let's implement a more practical example - a payment processing system that handles different types of payments:

csharp
public class PaymentProcessor
{
public virtual void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing generic payment for ${amount}");
// Generic payment processing logic
}
}

public class CreditCardProcessor : PaymentProcessor
{
public override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing credit card payment for ${amount}");
// Credit card specific processing logic
VerifyCardDetails();
ChargeCard(amount);
}

private void VerifyCardDetails()
{
Console.WriteLine("Verifying credit card details...");
// Verification logic
}

private void ChargeCard(decimal amount)
{
Console.WriteLine($"Charging credit card ${amount}");
// Charging logic
}
}

public class PayPalProcessor : PaymentProcessor
{
public override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing PayPal payment for ${amount}");
// PayPal specific processing logic
AuthenticateWithPayPal();
TransferFunds(amount);
}

private void AuthenticateWithPayPal()
{
Console.WriteLine("Authenticating with PayPal...");
// Authentication logic
}

private void TransferFunds(decimal amount)
{
Console.WriteLine($"Transferring ${amount} via PayPal");
// Transfer logic
}
}

public class BankTransferProcessor : PaymentProcessor
{
public override void ProcessPayment(decimal amount)
{
// Call base implementation for logging or common functionality
base.ProcessPayment(amount);

Console.WriteLine($"Processing bank transfer for ${amount}");
// Bank transfer specific logic
VerifyBankDetails();
InitiateTransfer(amount);
}

private void VerifyBankDetails()
{
Console.WriteLine("Verifying bank account details...");
// Verification logic
}

private void InitiateTransfer(decimal amount)
{
Console.WriteLine($"Initiating bank transfer for ${amount}");
// Transfer initiation logic
}
}

Usage:

csharp
static void Main(string[] args)
{
decimal paymentAmount = 125.75m;

// Simulate different payment methods
PaymentProcessor ccProcessor = new CreditCardProcessor();
PaymentProcessor ppProcessor = new PayPalProcessor();
PaymentProcessor btProcessor = new BankTransferProcessor();

Console.WriteLine("===== Processing different payment methods =====");

ccProcessor.ProcessPayment(paymentAmount);
Console.WriteLine();

ppProcessor.ProcessPayment(paymentAmount);
Console.WriteLine();

btProcessor.ProcessPayment(paymentAmount);

Console.ReadLine();
}

Output:

===== Processing different payment methods =====
Processing credit card payment for $125.75
Verifying credit card details...
Charging credit card $125.75

Processing PayPal payment for $125.75
Authenticating with PayPal...
Transferring $125.75 via PayPal

Processing generic payment for $125.75
Processing bank transfer for $125.75
Verifying bank account details...
Initiating bank transfer for $125.75

This example demonstrates how method overriding enables a common payment interface while allowing each payment method to implement its own processing logic. The client code can work with any payment processor without needing to know the specific type of payment being processed.

Common Pitfalls with Method Overriding

1. Forgetting the virtual keyword

If you forget to mark a base class method as virtual, the compiler will generate an error when you try to override it in a derived class.

csharp
public class Base
{
public void Method() { } // Missing virtual keyword
}

public class Derived : Base
{
// This will cause a compile-time error
public override void Method() { }
}

2. Confusing method overriding with method hiding

Method hiding (using the new keyword) is different from method overriding. Method overriding changes the implementation of the method for the derived type, while method hiding creates a new method that shadows the base class method.

csharp
public class Base
{
public virtual void Method()
{
Console.WriteLine("Base Method");
}
}

public class Derived : Base
{
// Method hiding
public new void Method()
{
Console.WriteLine("Derived Method");
}
}

When using method hiding:

csharp
Base b = new Derived();
b.Method(); // Outputs: "Base Method" (not overridden)

Derived d = new Derived();
d.Method(); // Outputs: "Derived Method"

3. Incorrect signature matching

For method overriding to work, the method signatures (name, return type, and parameters) must match exactly.

csharp
public class Base
{
public virtual int Method(int x)
{
return x;
}
}

public class Derived : Base
{
// This won't override because the parameter type is different
public override int Method(long x)
{
return (int)x;
}
}

Summary

Method overriding is a powerful feature of C# that enables polymorphic behavior in your object-oriented programs. With method overriding:

  • A derived class can provide a specific implementation of a method already defined in its base class
  • The virtual keyword marks a method in the base class as overridable
  • The override keyword indicates that a method in a derived class overrides a base class method
  • The base keyword allows access to the base class implementation
  • The sealed keyword prevents further overriding in derived classes

Method overriding is essential for implementing the polymorphism principle of OOP and creating flexible, extensible code. It allows you to design class hierarchies where specialized classes can modify the behavior inherited from their parent classes.

Exercises

  1. Create a Shape hierarchy with a Draw() method that is overridden by Circle, Rectangle, and Triangle classes to display different drawing messages.

  2. Implement a Document class hierarchy with a Save() method that is overridden by TextDocument, PDFDocument, and WordDocument classes.

  3. Design a Vehicle class with StartEngine() and StopEngine() methods, then create Car, Motorcycle, and ElectricVehicle classes that override these methods with appropriate implementations.

  4. Create a console application that demonstrates the use of abstract methods and method overriding to calculate taxes for different types of incomes.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)