C# Override Keyword: How to Use It
c# override keyword: Learn how the C# override keyword works with virtual methods, when to use it instead of new, and how to handle base calls, sealing, and object mem...
c# override keyword requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The override keyword in C# is used to replace the implementation of a virtual method in a derived class. It is the core mechanism for runtime polymorphism in the language. When you mark a method with override, you are telling the compiler that this method intentionally provides a new behavior for the inherited virtual member, and that calls through the base type will dispatch to this implementation.
The Virtual Method Contract
For override to work, the base method must be declared with the virtual keyword. A virtual method has a default implementation that derived classes can replace. The signature of the overriding method must match the base method exactly, including return type, parameter types, and accessibility. The override keyword is not optional; if you omit it, the compiler treats the method as a new method that hides the base member, which changes the dispatch behavior.
public class Animal { public virtual void Speak() { Console.WriteLine("..."); } } public class Dog : Animal { public override void Speak() { Console.WriteLine("Woof!"); } }
Here, Dog.Speak overrides Animal.Speak. When you call Speak on a Dog instance through an Animal reference, the runtime invokes the Dog implementation.
Override vs. New: Choosing the Right Keyword
The new keyword also allows a derived class to define a method with the same signature, but it does not participate in virtual dispatch. If you call the method through a base reference, the base implementation runs. This is a common source of confusion.
| Keyword | Dispatch behavior | Base method required | Typical use |
|---|---|---|---|
override | Virtual dispatch | Must be virtual or abstract | Extending or replacing behavior polymorphically |
new | Static dispatch | Any base method | Hiding a member intentionally, often to fix a design issue |
Use override when you want polymorphic behavior. Use new only when you have a specific reason to hide the base member and you understand that calls through base references will not reach your implementation.
Calling Base Implementations
Inside an overriding method, you can call the base implementation using the base keyword. This is useful when you want to extend the base behavior rather than replace it entirely.
public class Dog : Animal { public override void Speak() { base.Speak(); // runs the base implementation Console.WriteLine("Woof!"); } }
This pattern is common in constructors and lifecycle methods where the base class performs essential setup. However, be careful: calling base unconditionally can lead to unexpected behavior if the base method has side effects. Always consider whether the base call is necessary.
Sealing and Abstract Overrides
You can stop further overriding by marking an override as sealed. This prevents derived classes from overriding the method again.
public class Dog : Animal { public sealed override void Speak() { Console.WriteLine("Woof!"); } }
Now Dog.Speak cannot be overridden in a subclass of Dog.
Abstract methods are declared without an implementation and must be overridden in any non-abstract derived class. The override keyword is required when implementing an abstract method.
public abstract class Shape { public abstract double Area(); } public class Circle : Shape { public override double Area() { return Math.PI * Radius * Radius; } }
Overriding Object Members: Equals and GetHashCode
A common practical use of override is to customize value equality for your types. When you override Equals, you should also override GetHashCode to maintain the contract used by collections and hash-based structures.
public class Person { public string Name { get; set; } public override bool Equals(object obj) { if (obj is not Person other) return false; return Name == other.Name; } public override int GetHashCode() { return Name?.GetHashCode() ?? 0; } }
This is a simplified example. Real-world equality often involves multiple fields and null handling. The key point is that override gives you control over how your objects behave in collections like Dictionary and HashSet.
Runtime Cost of Virtual Dispatch
Virtual method calls have a small runtime overhead compared to non-virtual calls because the runtime must look up the method implementation through the object's type metadata. In most applications this cost is negligible, but in tight loops or high-frequency call sites it can matter.
The overhead is a single indirect jump through the vtable. Modern CPUs handle this well, but if you have a method that is called millions of times per second and does almost no work, the dispatch cost can become measurable. In such cases, you might consider making the method non-virtual or using a different design, but only after profiling confirms it is a bottleneck.
Maintainability and Design Considerations
The override keyword is a design tool. It signals that a method is part of a polymorphic contract. Overusing it can make hierarchies fragile, because changing a base method signature or behavior can ripple through many derived classes.
When designing a base class, mark methods as virtual only when you intend them to be extensible. If you are not sure, keep them non-virtual; you can always make them virtual later, but removing virtual is a breaking change. Similarly, when overriding, prefer to call base only when you need the base behavior. Overriding without a clear purpose adds complexity.
A common mistake is forgetting the virtual keyword on the base method. The compiler will not let you use override unless the base method is virtual or abstract. Another mistake is using a wrong signature; the override must match exactly, including parameter names are not required to match, but types and accessibility must.
Edge Cases: Overriding Generic Methods and Properties
The override keyword works with generic methods and properties as well. For a generic method, the override must have the same type parameter list. For properties, you override the accessors individually, but you cannot change accessibility of an accessor when overriding.
public class Base { public virtual T Get<T>(T value) => value; } public class Derived : Base { public override T Get<T>(T value) { return value; } }
Properties follow the same pattern:
public class Base { public virtual string Name { get; set; } } public class Derived : Base { public override string Name { get => base.Name; set => base.Name = value; } }
These cases are less common but important when building generic libraries or frameworks.
When Override Is Not the Right Tool
If you need to add a method with the same name but a different signature, that is overloading, not overriding. Overloading does not require virtual or override. Also, if you are working with an interface, you implement it using implements (implicitly) rather than override. The override keyword is specifically for class inheritance.
Understanding when not to use override is as important as knowing how to use it. For example, if you want to provide a new implementation for a method that is not virtual, you cannot override it; you can only hide it with new. That is usually a sign that the base class was not designed for extension.
The override keyword is a fundamental part of C# inheritance. Used correctly, it enables clean polymorphic designs. Used carelessly, it can create subtle bugs and maintenance problems. The rules are straightforward: the base method must be virtual or abstract, the signature must match, and you must explicitly mark the method with override to participate in virtual dispatch.