Optional parameters vs. method overloading

Two Fer
Two Fer in C#

Method overloading

Prior to C# 4.0, if you wanted a method to have a default value for one of its parameters, you needed to define an overload of that method (which the method overloading approach uses):

public static string Speak()
{
    return Speak("you");
}

public static string Speak(string name)
{
    return $"One for {name}, one for me.";
}

These two methods have the same name, but they have a different number of parameters. This allows the compiler to distinguish between calls to these methods based on the number of arguments passed to the method.

Optional parameters

C# 4.0 introduced optional parameters (which the optional parameter approach uses):

public static string Speak(string name = "you")
{
    return $"One for {name}, one for me.";
}

Using an optional parameter, we can assign a default value to that parameter which is then used if no value is passed for that parameter: Speak() and Speak("you") are equivalent. This new syntax allowed for a more succinct way of defining default values for parameters.

Note

Method overloading can do everything optional parameters can do, but the latter are arguably easier to read and write.

What are the differences?

The main difference between the two is that optional parameters are more concise. But what about their implementation? Are optional parameters just overloaded methods in disguise? Well, let's find out!

The most fool-proof way to check this is by examining the IL code (which is the intermediate code the .NET runtime executes) that both types of syntax compile to. A great way to check the generated IL code for a bit of C# code is by using sharplab.io. We created a sharplab gist for the following C# code:

public static class TwoFer
{
    public static string SpeakOverload()
    {
        return SpeakOverload("you");
    }

    public static string SpeakOverload(string name)
    {
        return $"One for {name}, one for me.";
    }

    public static string SpeakOptional(string name = "you")
    {
        return $"One for {name}, one for me.";
    }
}

We can then use the IL output option to view the generated IL code.

IL code: method overloading

This is the IL code for the two method overloading methods:

.method public hidebysig static
    string SpeakOverload () cil managed
{
    // Method begins at RVA 0x2094
    // Code size 16 (0x10)
    .maxstack 1
    .locals init (
        [0] string
    )

    IL_0000: nop
    IL_0001: ldstr "you"
    IL_0006: call string TwoFer::SpeakOverload(string)
    IL_000b: stloc.0
    IL_000c: br.s IL_000e

    IL_000e: ldloc.0
    IL_000f: ret
} // end of method TwoFer::SpeakOverload

.method public hidebysig static
    string SpeakOverload (
        string name
    ) cil managed
{
    // Method begins at RVA 0x20b0
    // Code size 22 (0x16)
    .maxstack 3
    .locals init (
        [0] string
    )

    IL_0000: nop
    IL_0001: ldstr "One for "
    IL_0006: ldarg.0
    IL_0007: ldstr ", one for me."
    IL_000c: call string [System.Runtime]System.String::Concat(string, string, string)
    IL_0011: stloc.0
    IL_0012: br.s IL_0014

    IL_0014: ldloc.0
    IL_0015: ret
} // end of method TwoFer::SpeakOverload

IL code: optional parameter

This is the IL code for the optional parameter method:

.method public hidebysig static
    string SpeakOptional (
        [opt] string name
    ) cil managed
{
    .param [1] = "you"
    // Method begins at RVA 0x20d4
    // Code size 22 (0x16)
    .maxstack 3
    .locals init (
        [0] string
    )

    IL_0000: nop
    IL_0001: ldstr "One for "
    IL_0006: ldarg.0
    IL_0007: ldstr ", one for me."
    IL_000c: call string [System.Runtime]System.String::Concat(string, string, string)
    IL_0011: stloc.0
    IL_0012: br.s IL_0014

    IL_0014: ldloc.0
    IL_0015: ret
} // end of method TwoFer::SpeakOptional

Comparison

The big differences are that the generated IL code:

  • does not include code that resembles the parameterless overloaded method's IL code,
  • is nearly identical to the single parameter, overloaded method's IL code, except for this one line:
.param [1] = "you"

This demonstrates that optional parameters are not overloaded methods in disguise.

Note

Note how the interpolated string is actually converted to a string.Concat call in the IL code.

Conclusion

Optional parameters are not only more elegant to use, they also result in a slightly smaller binary as less code is generated.

8th May 2024 · Found it useful?

Other Articles for Two Fer

Explore more ideas about this exercise