TL;DR:
- Method overriding in C# lets you customize or replace method behavior in child classes without changing the parent class.
- Mark the parent method as
virtual
orabstract
, and use theoverride
keyword in the child. - This enables runtime polymorphism—C# automatically calls the correct method based on the object’s actual type, not just its reference.
- Use it to extend frameworks, base classes, or shared APIs without rewriting existing logic.
In this guide, you’ll learn how to use method overriding in C#, when to use override
vs new
keywords, and how polymorphism works at runtime. Method overriding in C# is how a child class can change or extend what a parent class method does. When implementing method overriding in C#, you’ll need this whenever different classes need to handle the same action in their own way. In real-world applications, this means you can keep your interfaces consistent while letting each class implement things differently.
What is Method Overriding? C# Virtual Method Fundamentals
Method overriding is simply when a child class rewrites a method from its parent class. To override a method in C#, you need to keep the same method name, return type, and parameters, just change what the method actually does. The C# override keyword usage is essential for the compiler to understand your intent to replace the parent implementation.
classDiagram class BaseClass { +virtual void DoSomething() } class DerivedClassA { +override void DoSomething() } class DerivedClassB { +override void DoSomething() } BaseClass <|-- DerivedClassA BaseClass <|-- DerivedClassB note for BaseClass "Declares virtual method<br/>Defines base implementation" note for DerivedClassA "Overrides with specific implementation" note for DerivedClassB "Overrides with different implementation"
How Method Overriding Works in C#
What does this give you? With method overriding, you can:
- Create polymorphic behavior (same method name, different behaviors)
- Customize how inherited methods work without touching the parent code
- Follow the Open/Closed Principle, your code is open for extension but closed for modification
- Implement C# polymorphism override patterns where behavior changes based on the actual object type
Why Method Overriding Matters
Why should you care about method overriding? Here’s why it matters when building clean, maintainable code:
1. Makes Runtime Polymorphism Possible
With polymorphism, you can write code that says “call this method” without caring which specific class actually runs it. When you use a C# override method approach, the right version gets called based on the actual object type. This is different from method overloading where the selection happens at compile time—C# method override vs overload has this crucial distinction. With proper method overriding, you can add new types later without having to change your existing code.
sequenceDiagram participant Client as Client Code participant BR as BaseReference : BaseClass participant RT as Runtime participant BO as BaseObject participant DO as DerivedObject Client->>BR: Call virtual Method() BR->>RT: Which implementation to use? RT-->>BR: Check actual object type alt If reference points to BaseObject BR->>BO: Execute BaseObject.Method() BO-->>Client: Return base implementation result else If reference points to DerivedObject BR->>DO: Execute DerivedObject.Method() DO-->>Client: Return derived implementation result end Note over BR,RT: The same method call behaves differently<br/>based on the actual runtime type
Runtime Polymorphism with Method Overriding
2. Creates Clear Extension Points: How to Override Base Class Methods in C#
Good frameworks and libraries mark methods as virtual when they expect you might want to customize them. These C# virtual method declarations give you official “hooks” to change behavior without messing with the original source code. Understanding how to override base class methods in C# lets you effectively use these extension points.
3. Follows the Open/Closed Principle
The “O” in SOLID principles stands for Open/Closed: code should be open for extension but closed for modification. Method overriding is exactly this, you extend functionality without changing the original code.
4. Cuts Down on Copy-Paste Code
Instead of copying a big chunk of code just to change one small part, you can inherit everything and only override the bit that needs to be different. Less duplicated code means fewer bugs and easier maintenance.
5. Keeps Your Code Clean and Organized
With method overriding, you can clearly separate what needs to happen (defined in the base class) from how it happens (implemented in child classes). The parent lays out the structure, and each child fills in the details in its own way.
C# Method Overriding Examples: Real-World Scenarios
Let’s look at some practical C# method overriding examples that demonstrate these concepts in action.
Payment Processing System
Consider an e-commerce application that needs to handle multiple payment methods:
// Base payment processor
public abstract class PaymentProcessor
{
protected decimal _transactionFee;
public virtual decimal CalculateTransactionFee(decimal amount)
{
return _transactionFee;
}
public virtual async Task<PaymentResult> ProcessPaymentAsync(
PaymentDetails details, decimal amount, CancellationToken cancellationToken)
{
// Common validation logic
if (amount <= 0)
return new PaymentResult { Success = false, ErrorCode = "INVALID_AMOUNT" };
// Log the payment attempt - implementation removed for brevity
// This will be overridden by specific processors
return await ExecutePaymentAsync(details, amount, cancellationToken);
}
// Method to be overridden by derived classes
protected abstract Task<PaymentResult> ExecutePaymentAsync(
PaymentDetails details, decimal amount, CancellationToken cancellationToken);
}
// Credit card payment implementation
public class CreditCardProcessor : PaymentProcessor
{
public CreditCardProcessor()
{
_transactionFee = 0.025m; // 2.5%
}
public override decimal CalculateTransactionFee(decimal amount)
{
// Credit cards have a minimum fee
decimal standardFee = base.CalculateTransactionFee(amount);
return Math.Max(standardFee * amount, 0.50m);
}
protected override async Task<PaymentResult> ExecutePaymentAsync(
PaymentDetails details, decimal amount, CancellationToken cancellationToken)
{
var cardDetails = details as CreditCardDetails
?? throw new ArgumentException("Invalid payment details");
// Credit card-specific implementation - details removed for brevity
// Connect to credit card gateway, process payment, handle response
return new PaymentResult { Success = true };
}
}
// PayPal implementation
public class PayPalProcessor : PaymentProcessor
{
public PayPalProcessor()
{
_transactionFee = 0.029m; // 2.9%
}
public override decimal CalculateTransactionFee(decimal amount)
{
// PayPal charges percentage + fixed fee
return (base.CalculateTransactionFee(amount) * amount) + 0.30m;
}
protected override async Task<PaymentResult> ExecutePaymentAsync(
PaymentDetails details, decimal amount, CancellationToken cancellationToken)
{
var paypalDetails = details as PayPalDetails
?? throw new ArgumentException("Invalid payment details");
// PayPal-specific implementation - details removed for brevity
return new PaymentResult { Success = true };
}
}
How Method Overriding Is Used in Production
In the above example, a developer might use these payment processors like this:
public class OrderService
{
private readonly Dictionary<PaymentMethod, PaymentProcessor> _processors;
public OrderService(IEnumerable<PaymentProcessor> availableProcessors)
{
// Initialize processors - implementation removed for brevity
}
public async Task<OrderResult> ProcessOrderAsync(
Order order, PaymentDetails paymentDetails, CancellationToken cancellationToken)
{
// Get the appropriate payment processor based on the customer's choice
if (!_processors.TryGetValue(order.PaymentMethod, out var processor))
{
return new OrderResult { Success = false, Error = "Payment method not supported" };
}
// Process payment - the correct implementation runs based on the processor type
// This is where polymorphism happens - the right ExecutePaymentAsync runs
var paymentResult = await processor.ProcessPaymentAsync(
paymentDetails, order.TotalAmount, cancellationToken);
// Result handling - removed for brevity
return new OrderResult { Success = true };
}
}
Notice how the runtime type of the object determines which implementation gets executed.
C# Method Overriding Rules
For successful C# inheritance override method implementations, follow these essential rules:
- Mark parent methods with
virtual
,abstract
, oroverride
- Without this, C# won’t let you override them - Add the
override
keyword in child methods - This tells the compiler “Yes, I’m intentionally overriding this” - Keep the signature exactly the same - Same method name, return type, and parameters (no changes allowed)
- Don’t reduce accessibility - Your override can be more accessible than the parent method, but never less
- Understand C# override vs new differences - The
new
keyword hides rather than overrides, breaking the polymorphic behavior
flowchart TD Start([Start]) --> BaseVirtual{Base method<br>virtual/abstract?} BaseVirtual -->|No| CantOverride[Cannot be overridden] BaseVirtual -->|Yes| ChildOverride{Child has<br>override keyword?} ChildOverride -->|No| MethodHiding[Method hiding<br>with 'new' keyword] ChildOverride -->|Yes| SignatureMatch{Signatures<br>match?} SignatureMatch -->|No| CompileError[Compile Error] SignatureMatch -->|Yes| AccessLevel{Child method<br>accessibility} AccessLevel -->|Less than base| CompileError AccessLevel -->|Equal or greater| SuccessfulOverride[Successful Override] SuccessfulOverride --> RuntimeCall[Runtime: Call appropriate<br>implementation based<br>on actual object type] MethodHiding --> BaseMethodCalled[Runtime: Base or child method<br>called based on reference type] style CompileError fill:#f55,stroke:#333,stroke-width:2px style SuccessfulOverride fill:#5d5,stroke:#333,stroke-width:2px style MethodHiding fill:#fa5,stroke:#333,stroke-width:2px style BaseMethodCalled fill:#fa5,stroke:#333,stroke-width:2px style RuntimeCall fill:#5d5,stroke:#333,stroke-width:2px
Method Overriding Resolution Process in C#
C# Inheritance Override Method: Notification System Example
Most applications need to send notifications in different ways. Method overriding works great for this:
public abstract class NotificationService
{
protected readonly ILogger _logger;
protected readonly NotificationSettings _settings;
protected NotificationService(ILogger logger, NotificationSettings settings)
{
_logger = logger;
_settings = settings;
}
// Template method pattern with parts that can be overridden
public async Task<NotificationResult> SendNotificationAsync(
NotificationRequest request, CancellationToken cancellationToken)
{
try
{
// Common validation - removed for brevity
// Format the notification (overridden by subclasses)
var formattedContent = FormatNotificationContent(request);
// Send notification (implementation varies by channel)
bool success = await SendAsync(formattedContent, request, cancellationToken);
return new NotificationResult(success);
}
catch (Exception ex)
{
return new NotificationResult(false, ex.Message);
}
}
// These methods are overridden by specific notification channels
protected abstract Task<bool> SendAsync(
string formattedContent,
NotificationRequest request,
CancellationToken cancellationToken);
protected virtual string FormatNotificationContent(NotificationRequest request)
{
return request.Message;
}
}
// Email notification implementation
public class EmailNotificationService : NotificationService
{
private readonly IEmailClient _emailClient;
public EmailNotificationService(
ILogger<EmailNotificationService> logger,
NotificationSettings settings,
IEmailClient emailClient) : base(logger, settings)
{
_emailClient = emailClient;
}
protected override string FormatNotificationContent(NotificationRequest request)
{
// Add email-specific formatting like HTML
var baseContent = base.FormatNotificationContent(request);
return $"<html><body>{baseContent}</body></html>";
}
protected override async Task<bool> SendAsync(
string formattedContent,
NotificationRequest request,
CancellationToken cancellationToken)
{
// Email-specific implementation - details removed for brevity
return await _emailClient.SendEmailAsync(new EmailRequest(), cancellationToken);
}
}
// Push notification implementation
public class PushNotificationService : NotificationService
{
private readonly IPushNotificationProvider _pushProvider;
public PushNotificationService(
ILogger<PushNotificationService> logger,
NotificationSettings settings,
IPushNotificationProvider pushProvider) : base(logger, settings)
{
_pushProvider = pushProvider;
}
protected override string FormatNotificationContent(NotificationRequest request)
{
// Push notifications need to be shorter
var content = base.FormatNotificationContent(request);
return content.Length <= 100 ? content : content.Substring(0, 97) + "...";
}
protected override async Task<bool> SendAsync(
string formattedContent,
NotificationRequest request,
CancellationToken cancellationToken)
{
// Push notification-specific implementation - details removed for brevity
return await _pushProvider.SendPushNotificationAsync(new PushRequest(), cancellationToken);
}
}
C# Override vs Overload Difference: Choosing the Right Pattern
Method overriding isn’t the only way to customize behavior. Understanding the C# override vs overload difference is just the start—there are other patterns to consider as well. Here’s how method overriding compares to other common patterns:
flowchart TB Start([Need to customize behavior?]) --> NeedInheritance{Is inheritance<br>appropriate?} NeedInheritance -->|Yes| NeedStability{Is base class<br>hierarchy stable?} NeedInheritance -->|No| UseStrategy[Use Strategy Pattern<br>Composition over Inheritance] NeedStability -->|Yes| UseOverride[Use Method Overriding<br>virtual/override keywords] NeedStability -->|No| NeedAddBehavior{Need to add<br>behavior dynamically?} NeedAddBehavior -->|Yes| UseDecorator[Use Decorator Pattern<br>Wrap objects with new behavior] NeedAddBehavior -->|No| UseStrategy classDef decision fill:#999,stroke:#333,stroke-width:2px classDef solution fill:#bbf,stroke:#333,stroke-width:2px class NeedInheritance,NeedStability,NeedAddBehavior decision class UseOverride,UseStrategy,UseDecorator solution
Comparing Method Overriding with Alternative Patterns
Pattern | When to Use | Pros | Cons |
---|---|---|---|
Method Overriding | When you have a stable base class hierarchy and need to customize behavior in derived classes | Simple, clean, built into the language | Creates tight coupling, requires inheritance |
Strategy Pattern | When you need to switch algorithms at runtime or avoid inheritance | More flexible, no inheritance required | More complex, more objects |
Decorator Pattern | When you need to add responsibilities without subclassing | Flexible composition, adheres to SRP | Can lead to many small classes |
Method Overloading | When the same operation needs different parameter sets | Clear API, compile-time resolution | No runtime polymorphism like C# method override |
When Should You Use C# Override Sealed Method or Abstract Methods?
Method overriding is your best choice when:
- You have a clear parent-child relationship between your classes
- Your base class already does most of the work right, with just parts needing customization
- You need different classes to handle the same method in their own way
- You want to ensure all classes follow the same pattern or protocol
- You need to decide which implementation to run based on the object’s type at runtime
Remember that you cannot use C# override sealed methods as they’re specifically designed to prevent further specialization. Also, C# abstract class method override patterns are particularly useful when you want to define a structure that requires implementation by derived classes.
Method Overriding Best Practices in C#: C# Base Method Call in Override
- Respect the Contract: Don’t change what the method is supposed to do, just how it does it
- Call Base When Appropriate: Use base method call in override (
base.Method()
) when you want to add to the parent behavior, not replace it entirely - Use Protected Virtual Methods: Create “hook points” in your base class that child classes can customize
- Document Your Intent: Comment on why a method is virtual and how others should override it
- Add Sealed When Needed: Use the
sealed
keyword to say “you can override my methods, but not this specific one” - Watch Your Dependencies: Make sure your override doesn’t accidentally break something the base class needs
- Match Signatures Precisely: When you C# override method implementations, ensure parameter types and order match exactly
The Bottom Line
Method overriding is essential to object-oriented programming and it’s what makes polymorphism possible. The C# inheritance override method pattern creates a powerful mechanism for specialization. With overriding, your parent class sets the rules, and child classes fill in the details.
Look at any well-built application (like the payment and notification examples above), and you’ll see method overriding at work. The beauty of it? You can add new types tomorrow without touching today’s code. It’s the perfect balance between consistency and flexibility.
Whether you’re working with C# virtual methods in a framework or implementing your own abstract class hierarchies, proper method overriding is a core skill for any C# developer building maintainable, flexible systems.
Frequently Asked Questions
What is method overriding in C#?
virtual
, and the derived method must use the override
keyword. This enables polymorphic behavior.How do you override a method in C#?
To override a method, declare the base class method as virtual
and the derived class method as override
with the same signature. Example:
public class Animal { public virtual void Speak() { } }
public class Dog : Animal { public override void Speak() { Console.WriteLine("Woof"); } }
What is the difference between override and new keywords in C#?
override
keyword replaces a virtual method from the base class, enabling polymorphism. The new
keyword hides the base method, but does not provide polymorphic behavior. Use override
for true method overriding. see here.Why is method overriding important in object-oriented programming?
Can you override a non-virtual method in C#?
virtual
, abstract
, or override
in the base class can be overridden. Attempting to override a non-virtual method will result in a compile-time error.What happens if you do not use the override keyword in the derived class?
override
but declare a method with the same signature, the new method hides the base method instead of overriding it. This can lead to unexpected behavior when using base class references.How does polymorphism work with method overriding in C#?
Polymorphism allows you to use a base class reference to call overridden methods in derived classes. The actual method executed is determined at runtime based on the object’s type.
Animal a = new Dog(); a.Speak(); // Calls Dog's Speak
Can constructors be overridden in C#?
virtual
can be overridden.What is the purpose of the base keyword in method overriding?
The base
keyword allows a derived class to call the implementation of a method from its base class. This is useful when you want to extend, not completely replace, the base behavior.
public override void Speak() { base.Speak(); Console.WriteLine("Extra"); }
When should you use method overriding in C#?
Related Posts
- When Inheritance Still Makes Sense in C#: Polymorphism Without Swapping
- Abstract Classes in C#: When and How to Use Them Effectively [+Examples]
- Object-Oriented Programming: Core Principles and C# Implementation
- Polymorphism in C#: How Template Method, Strategy, and Visitor Patterns Make Your Code Flexible
- Override vs New Keywords in C#: Method Hiding vs Overriding