Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is replacing an optional parameter with overloads a breaking change?

I am aware that adding an optional parameter in a library method is a breaking change,

void Foo(int x)             // OLD void Foo(int x, int y = 5)  // NEW 

because in the compiled code the new version is seen as Foo(int, int). Every call of Foo(0) (source code) is translated to Foo(0, 5) (compiled code) by the compiler. Thus, an old client, using a compiled call of Foo(0) would not find a suitable method.


What about the other direction?

void Foo(int x, int y = 5) { ... }    // OLD  void Foo(int x)        { Foo(x, 5); } // NEW void Foo(int x, int y) { ... }        // NEW 

Foo(0) (source code) would still compile, and Foo(0, 5) (compiled code) would still find a suitable overload, so, theoretically, this should work.

Does it work in practice, i.e., is this scenario "officially supported" by the .NET runtime and the C#/VB compilers? Or are calls to methods with optional parameters somehow "marked", causing them to fail when the optional parameters are replaced by overloads?


EDIT: To clarify, I'm asking about binary compatibility: Is it possible to replace library.dll (old) with library.dll (new) without recompiling projectUsingLibrary.exe?

like image 971
Heinzi Avatar asked Oct 01 '12 09:10

Heinzi


People also ask

Is adding an optional parameter a breaking change?

If you have a method with optional parameters, you can never add an overload with additional optional parameters out of fear of causing a compile-time breaking change. And you can never remove an existing overload, as this has always been a runtime breaking change. You pretty much need to treat it like an interface.

Are optional parameters bad practice?

The thing with optional parameters is, they are BAD because they are unintuitive - meaning they do NOT behave the way you would expect it. Here's why: They break ABI compatibility ! so you can change the default-arguments at one place.

What is an optional parameter in a function?

What are Optional Parameters? By definition, an Optional Parameter is a handy feature that enables programmers to pass less number of parameters to a function and assign a default value.

What are the differences between mandatory parameters and optional parameters?

A mandatory parameter must be explicitly given on the command-line, otherwise an error message will be printed to prompt the user to re-enter the command. If an optional parameter is not specified, the default is used. Note: Mandatory parameters have a default value for use in the GUI.


Video Answer


1 Answers

I thought that was a good question, so here goes my take.

Using a quick client that does this:

        c1.Foo(1);         c1.Foo(1, 2); 

When using optional parameter the client IL looks like:

    IL_0000: nop IL_0001: newobj instance void [ClassLibrary1]ClassLibrary1.Class1::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: ldc.i4.5 IL_000a: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32) IL_000f: nop IL_0010: ldloc.0 IL_0011: ldc.i4.1 IL_0012: ldc.i4.2 IL_0013: callvirt instance void [ClassLibrary1]ClassLibrary1.Class1::Foo(int32, int32) IL_0018: nop IL_0019: ret 

and when using overloads it looks like:

    IL_0000: nop IL_0001: newobj instance void [ClassLibrary2]ClassLibrary2.Class2::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32) IL_000e: nop IL_000f: ldloc.0 IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: callvirt instance void [ClassLibrary2]ClassLibrary2.Class2::Foo(int32, int32) IL_0017: nop IL_0018: ret 

So, if you changed the implementation from optional to overloads, but left the client as it was originally, it would be effectively adding the default parameter for you, and always calling the the function that has two arguments, which may or may not be the desired behaviour.

like image 173
Justin Harvey Avatar answered Sep 21 '22 11:09

Justin Harvey