Optional argument corner cases, part four

Last time we discussed how some people think that an optional argument generates a bunch of overloads that call each other. People also sometimes incorrectly think that

void M(string format, bool b = false) 
  Console.WriteLine(format, b); 

is actually a syntactic sugar for something morally like:

void M(string format, bool? b) 
  bool realB = b ?? false; 
  Console.WriteLine(format, realB); 

and then the call site


is rewritten as

M("{0}", null};

That is, they believe that the default value is somehow “baked in” to the callee.

In fact, the default value is baked in to the caller; the code on the callee side is untouched and the caller becomes

M("{0}", false);

A consequence of this fact is that if you change the default value of a library method without recompiling the callers of that library, the callers don’t change their behaviour just because the default changed. If you ship a new version of method M that changes the default to true it doesn’t matter to those callers. Until a caller of M with one argument is recompiled it will always pass false.

That could be a good thing. Changing a default from false to true is a breaking change, and one could argue that existing callers should be insulated from that breaking change.

This is a fairly serious versioning issue, and one of the main reasons why we pushed back for so long on adding default arguments to C#. The lesson here is to think carefully about the scenario with the long term in mind. If you suspect that you will be changing a default value and you want the callers to pick up the change without recompilation, don’t use a default value in the argument list; make two overloads, where the one with fewer parameters calls the other.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s