C#
optional parameters
overridden methods
object-oriented programming
programming techniques

C optional parameters on overridden methods

Master System Design with Codemia

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

Introduction

Optional parameters and method overriding look compatible in C#, but they interact in a way that surprises many developers. The key rule is that the default argument value is chosen at compile time from the static type of the reference, not from the runtime type of the object.

That means an override can change behavior in the method body, but it does not change which optional default gets substituted at the call site. If you miss that rule, virtual dispatch and default arguments appear to disagree with each other.

Why Optional Parameters Behave This Way

In C#, optional arguments are not dynamically looked up at runtime. The compiler inserts the default value into the call based on the method signature visible at compile time.

Consider this example:

csharp
1using System;
2
3class BasePrinter
4{
5    public virtual void Print(string message = "base")
6    {
7        Console.WriteLine($"Base: {message}");
8    }
9}
10
11class DerivedPrinter : BasePrinter
12{
13    public override void Print(string message = "derived")
14    {
15        Console.WriteLine($"Derived: {message}");
16    }
17}
18
19class Program
20{
21    static void Main()
22    {
23        BasePrinter a = new DerivedPrinter();
24        DerivedPrinter b = new DerivedPrinter();
25
26        a.Print();
27        b.Print();
28    }
29}

The override runs in both cases, but the default value used for a.Print() comes from BasePrinter, because the compiler sees a as a BasePrinter. The default value for b.Print() comes from DerivedPrinter.

So the output reflects both compile-time binding of optional arguments and runtime virtual dispatch of the method body.

Why This Can Be Confusing

Most developers expect overriding to behave entirely at runtime. That expectation is reasonable for ordinary virtual dispatch, but optional arguments are different because they are partly a call-site feature.

The compiler effectively rewrites the call to include the missing argument. Once that happens, the overridden method simply receives an explicit value.

That is why this topic is less about whether overrides are allowed and more about whether changing default values on virtual methods is a good idea. Usually it is not.

A Safer Design

The safest approach is to avoid optional parameters on virtual or abstract methods when the default value is part of the API contract. Instead, use overloads:

csharp
1using System;
2
3class BasePrinter
4{
5    public void Print()
6    {
7        PrintCore("base");
8    }
9
10    protected virtual void PrintCore(string message)
11    {
12        Console.WriteLine($"Base: {message}");
13    }
14}
15
16class DerivedPrinter : BasePrinter
17{
18    protected override void PrintCore(string message)
19    {
20        Console.WriteLine($"Derived: {message}");
21    }
22}

Now the default value lives in one non-virtual entry point, and the polymorphic part is separated into the core method. That design is easier to reason about.

When Keeping Optional Parameters Is Acceptable

If a virtual method already has an optional parameter, the least surprising choice is to keep the same default value in the override. That does not change how compile-time binding works, but it reduces inconsistency between reference types.

In other words, the feature is legal, but changing the default value in the override is what creates confusion.

Common Pitfalls

The most common mistake is assuming the override's default value will always be used. It will not if the call is compiled against the base type.

Another mistake is treating optional parameters as if they were part of runtime polymorphism. They are resolved before virtual dispatch happens.

A third issue is exposing virtual methods with optional parameters in public APIs and later changing the default value. Existing callers may keep the old default because it was compiled into their call sites.

Summary

  • Optional argument values in C# are chosen at compile time from the static type.
  • Overridden methods still use virtual dispatch for the method body.
  • Changing the default value in an override is legal but often confusing.
  • Overloads plus a virtual core method are usually a cleaner design.
  • If you keep optional parameters on virtual methods, avoid changing their defaults across the hierarchy.

Course illustration
Course illustration

All Rights Reserved.