c# virtual vs override: What Each Modifier Does
c# virtual vs override: Explains the difference between virtual and override in C#, when to use each, and how they affect method dispatch in derived classes.
The distinction between c# virtual vs override is one of the first places where inheritance behavior becomes non-obvious. The two modifiers work together, but they answer different questions: virtual declares that a method can be redefined in a derived class, while override replaces the base implementation in the derived class. Understanding that difference is essential for controlling runtime method dispatch.
The Syntax of virtual and override
A virtual method is declared in a base class and provides a default implementation that derived classes may replace. An override method is declared in a derived class and explicitly replaces the base version. The syntax is straightforward:
public class Shape { public virtual double Area() { return 0; } } public class Circle : Shape { private double radius; public Circle(double radius) { this.radius = radius; } public override double Area() { return Math.PI * radius * radius; } }
The base Shape.Area() returns zero as a placeholder. The derived Circle.Area() overrides it with a real calculation. Without virtual in the base class, the derived class cannot use override; it would have to use new to hide the method, which changes the dispatch behavior entirely.
How Method Dispatch Works at Runtime
The key difference between virtual and override is how the runtime selects which method to call. When a method is marked virtual, the runtime uses the actual type of the object, not the type of the variable, to determine which implementation to invoke. This is called dynamic dispatch.
Shape shape = new Circle(5); Console.WriteLine(shape.Area()); // Calls Circle.Area()
Even though shape is declared as Shape, the runtime sees that the actual object is a Circle and calls the overridden method. If Area had been hidden with new instead of overridden, the call would resolve to Shape.Area() because the variable type is Shape. This is the central behavioral difference that developers confuse when comparing c# virtual vs override.
When to Use virtual Without override
Marking a method virtual does not force any derived class to override it. It only permits it. This is useful when you want to provide a sensible default behavior while leaving the door open for customization. A common example is a template method pattern where the base class calls a virtual hook that derived classes may optionally override.
public class ReportGenerator { public void GenerateReport() { PrepareData(); WriteHeader(); WriteBody(); WriteFooter(); } protected virtual void WriteHeader() { Console.WriteLine("Standard Header"); } protected virtual void WriteBody() { Console.WriteLine("Standard Body"); } } public class CustomReportGenerator : ReportGenerator { protected override void WriteBody() { Console.WriteLine("Custom Body"); } }
Here, WriteHeader remains the default, while WriteBody is overridden. The base class controls the overall algorithm, and derived classes only customize specific steps. This is a legitimate use of virtual without requiring every derived class to override every method.
When override Is Required: Abstract Methods
An abstract method is implicitly virtual but has no implementation. Any non-abstract derived class must override it. This is a stronger contract than a simple virtual method.
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() { return Width * Height; } }
In this case, override is not optional; it is required for the class to be instantiable. The distinction between virtual and abstract is important because abstract forces the derived class to provide an implementation, while virtual only allows it.
Common Mistakes: Forgetting base and Incorrect Signatures
A frequent error is forgetting to call base.Method() inside an override when the base implementation performs necessary work. The runtime does not automatically call the base version; you must do it explicitly if needed.
public class BaseLogger { public virtual void Log(string message) { // Write to file } } public class TimestampLogger : BaseLogger { public override void Log(string message) { base.Log($"{DateTime.Now}: {message}"); // Explicitly call base } }
Another mistake is mismatching the method signature. The override must have the same name, return type, and parameter list as the virtual method. If you change any of these, the compiler treats it as a new method, not an override, and you will get a warning unless you use the new keyword.
Overriding vs Hiding: The new Modifier
When a derived class declares a method with the same signature as a base method but does not use override, it hides the base method. This is called shadowing. The new modifier makes the intent explicit.
public class Base { public virtual void Display() { Console.WriteLine("Base"); } } public class Derived : Base { public new void Display() { Console.WriteLine("Derived"); } }
If you call Display through a Base variable, the base version runs; through a Derived variable, the derived version runs. This is static dispatch based on the variable type. Hiding is rarely the intended behavior because it breaks polymorphism. Prefer override whenever you want the derived implementation to be used polymorphically.
Sealed Override and Further Inheritance
An override can be marked sealed to prevent further overrides in subsequent derived classes. This is useful when you want to lock down a particular implementation after a certain level of inheritance.
public class A { public virtual void Foo() { } } public class B : A { public sealed override void Foo() { } } public class C : B { // Cannot override Foo here; compiler error }
Sealing an override is a design decision that communicates that the behavior is final. It can also enable certain compiler optimizations because the method call can be statically bound in some cases, though you should not rely on that without measuring.
Maintainability and Design Considerations
Choosing between virtual and override is not just a syntax decision; it shapes how your class hierarchy evolves. Every virtual method is an extension point that subclasses may override. Too many virtual methods make the base class fragile because changing the base implementation can break derived classes that depend on the old behavior. Too few virtual methods make the hierarchy rigid and force subclassing to use new hiding, which leads to subtle bugs.
A practical guideline is to mark methods as virtual only when you have a concrete extension scenario in mind. If you are not sure, keep the method non-virtual. You can always make it virtual later without breaking existing callers, but removing virtual from a public API is a breaking change.
Also consider the runtime cost. Virtual method calls have a small overhead compared to nonvirtual calls because of the indirection through the type's vtable. In performance-sensitive code paths, excessive virtual calls can matter, but the impact is usually negligible unless the method is called millions of times in a tight loop. Profile before optimizing; do not avoid override for speculative performance reasons.
Finally, remember that override works with both virtual and abstract methods. The decision between these two depends on whether the base class provides a default implementation. Use abstract when the concept has no meaningful default and every derived class must supply its own behavior. Use virtual when a default exists but can be replaced.
The distinction between c# virtual vs override is one of the first places where inheritance behavior becomes non-obvious. The two modifiers work together, but they answer different questions: virtual declares that a method can be redefined in a derived class, while override replaces the base implementation in the derived class. Understanding that difference is essential for controlling runtime method dispatch.