.NET Framework 4 came with the new features of Optional arguments and Named parameters. First, I will tell in brief what these features are before I go to their use in interface which I want to highlight. In fact, it is not .NET 4, but C# 4.0 has come with this feature, that's why while using Visual Studio 2010 this feature can be used with .NET Framework 3.5, 3.0 and even 2.0.
Named Arguments: Starting from .NET 4, we can specify method parameter specifying their name explicitly. This helps in increasing readability of code as well as the consumer of method does not need to remember the sequence of parameter. Look at the below method (in C#):
Following are all the cases in which we can call it using named arguments:
Using named arguments, we can pass arguments in any order. Or we can pass parameter as positional followed by named arguments. Just note that named arguments cannot be followed by positional arguments and also while mixing positional and named arguments, we cannot alter the sequence. We have to pass positional arguments as per their position. Look at the code above and look at the last two calls (call 4 and call 5). In call 4, we are not passing the positional argument in correct order. While in call 5, the named argument is followed by positional argument which is not allowed, Both of these method calls will generate compile time error.
Optional Arguments: The second related feature introduced in .NET 4 is Optional Arguments, Now we can specify default value of an argument for a method. While calling that method, we have a freedom if we want to pass that parameter or just take advantage of default parameter. Look at the below method to see how to define default value of optional arguments I have modified the
GetBonus method to provide multiplier 2 as default value.
Now when calling this method, we can pass this parameter or we may leave it. Look below at how I have called this method.
This is a great feature which will help us to avoid some of the overloaded methods. At first look, it seems that the compiler has generated overloads for us to handle this situation, but that is not the case.
Let's see how the IL looks when I try to open the assembly using ILDASM and look at the compiled code of
This IL code clearly shows that the compiler has picked the default values where we have not provided the values for optional arguments and embedded that into the calling code. So we need to take precaution when we are just trying to change the default value in the class defining the method having optional arguments and replace this assembly only, and not recompiling the assembly of callable code. In that case, the old default values will be used as that still will be present embedded in calling code.
Remember named arguments can be used to solve a situation in which we want to skip one of the arguments in between and want to pass them later. look at the example below:
What if I want to pass
yearsOfService but do not want to pass multiplier. If I use call 1 below the parameter 3 will be automatically taken for multiplier but this is not what I wanted.
The solution for this kind of situation will be to use named argument. In call 2, I am passing the required parameter
yearsOfService is passed as named argument. Now I have not passed multiplier as I wanted and I am able to modify default value of
Precedence of Optional Arguments while using Interfaces: Now think of a scenario in which I define the
GetBonus method into an interface and I also specify the default parameters too. Later, I implemented this interface in a class and specify default parameters there too. Let's look at the code below. I define an interface
GetBonus method with default value of multiplier and
yearsofService as below:
I implement this method into a class
SalaryCalculator. Note that I have defined the default parameters again in this implementation the value for them is different than what I defined previously in the interface.
Now the question is if I call the method of class
SalaryCalculator which default values will be used? Let's see by writing a small consumable for this. Look at the below code in which I am calling this method once using class instance and later via interface.
Look at the output below:
We can clearly see that if we call the method using class instance directly default parameters specified in the method implementation will be used and if I call the method by casting the class instance to interface, default values specified in the interface declaration will be used!! The above lines of code violate the "Liskov substitution principle" we should avoid using default parameter values this way. Now where you should give this depends upon your need. Sometimes, people avoid using optional arguments in interfaces because
public methods should be avoided with optional arguments for obvious reasons. Others want to give the entire definition to interfaces itself. It depends upon the requirement but the gist is that it should not be at both places. Optional arguments were part of many languages from a long time like VB.NET, C# got it latest. Also named arguments are also a great feature because of two reasons; one because without it optional arguments are not complete and the other is that they are providing a great deal of readability to method call which makes maintenance very easy.