C#
programming
software development
object-oriented programming
method implementation

Why C implements methods as non-virtual by default?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

C# methods are non-virtual by default because virtual dispatch is a design choice, not the safest universal default. Making methods virtual opens an inheritance contract: derived classes can override behavior, base classes must tolerate that, and future changes become more constrained. C# therefore requires the programmer to opt into virtual behavior explicitly.

Virtual Methods Are an API Commitment

When a method is marked virtual, you are not only enabling polymorphism. You are promising that derived classes may replace that behavior and that your base class design can handle it safely.

csharp
1public class ReportFormatter
2{
3    public virtual string Format(string input)
4    {
5        return input.Trim();
6    }
7}
8
9public class HtmlReportFormatter : ReportFormatter
10{
11    public override string Format(string input)
12    {
13        return $"<strong>{input.Trim()}</strong>";
14    }
15}

That is useful when extensibility is part of the design. But it also means the base class author must think about override safety, call order, invariants, and documentation.

If every method were virtual automatically, many APIs would expose extension points they never intended to support.

Non-Virtual by Default Supports Encapsulation

Most methods are implementation details, not variation points. Keeping them non-virtual by default protects internal logic from accidental override and makes class behavior easier to reason about.

csharp
1public class InvoiceCalculator
2{
3    public decimal CalculateTotal(decimal subtotal, decimal tax)
4    {
5        return subtotal + tax;
6    }
7}

This communicates a simple contract: callers can use the method, but derived classes are not expected to redefine the calculation.

That default helps developers expose only the extension points they mean to support.

Versioning Is Easier When Methods Are Closed

Virtual methods complicate versioning. Once a method is overridable, later changes to how and when it is called can break subclasses in subtle ways. Base-class evolution becomes harder because derived classes may depend on behavior that was never meant to be externally customized.

Non-virtual defaults reduce that fragility. Library authors can later decide to make a method virtual if they intentionally want to open that seam. The reverse direction is much harder.

This is closely related to the classic fragile-base-class problem: inheritance creates coupling between base and derived classes that is hard to reason about across versions.

Performance Is a Secondary Reason, Not the Main One

Non-virtual calls can sometimes be optimized more aggressively than virtual calls, and historically that mattered more. Modern runtimes can devirtualize in many situations, so raw dispatch speed is not the main design argument.

The stronger reason is semantic clarity. C# wants virtual to mean something deliberate. A reader of the code should be able to spot where polymorphic substitution is expected.

Prefer Composition Unless Inheritance Is Intentional

One reason this language design works well is that many customization problems are better solved with interfaces, delegates, or composition instead of overrides.

csharp
1public interface IDiscountPolicy
2{
3    decimal Apply(decimal subtotal);
4}
5
6public class OrderService
7{
8    private readonly IDiscountPolicy policy;
9
10    public OrderService(IDiscountPolicy policy)
11    {
12        this.policy = policy;
13    }
14
15    public decimal Calculate(decimal subtotal)
16    {
17        return policy.Apply(subtotal);
18    }
19}

This avoids turning every method into an inheritance hook. The design is explicit about what varies.

Common Pitfalls

  • Assuming methods should be virtual by default because inheritance is available.
  • Exposing virtual methods without documenting the override contract or preserving class invariants.
  • Using inheritance for customization when composition or interfaces would create less coupling.
  • Treating performance as the only reason for non-virtual defaults.
  • Forgetting that virtual methods constrain how a base class can evolve over time.

Summary

  • C# uses non-virtual methods by default so overridability is explicit, not accidental.
  • A virtual method is an extensibility contract, not just a dispatch mechanism.
  • Closed methods are easier to reason about, version, and maintain safely.
  • Performance matters, but API design and inheritance safety matter more.
  • Mark methods virtual only when overriding is a deliberate part of the class design.

Course illustration
Course illustration

All Rights Reserved.