Optional Parameters and Named Arguments in C# 4.0

Optional Parameters and Named Arguments

 
When a function has an optional parameter, the caller can decide whether to provide a value or not. (Providing a value for the parameter is optional.) If the caller doesn't provide a value, the function uses a default value.
 
C++ allows optional parameters. In this example, b is an optional parameter. If the caller doesn't provide a value for b, the function uses the default value, zero.
  1. int DoSomething(int a, int b = 0)  
  2. {  
  3.     return a + b;   
  4. }  
This can be called as follows:
  1. // Returns 5 + 7 = 12;  
  2. int x = DoSomething(5, 7);  
  3.    
  4. // Returns 5 + 0 = 5  
  5. int y = DoSomething(5);  
VB.Net has allowed optional parameters since version 1.0. C#, however, did not support optional parameters until .NET 4.0.
 

Method Overloading

 
Prior to .NET 4.0, the closest you could come to optional parameters was method overloading. Here, for example, the method DoSomething has an optional parameter with a default value of 0.
  1. public int DoSomething(int a, int b)  
  2. {  
  3.         return a + b;  
  4. }  
  5.    
  6. public int DoSomething(int a)  
  7. {  
  8.         Return DoSomething(a, 0);  
  9. }  
Method overloading has its problems. For one thing, every optional parameter can double the number of overloads. DoSomething( ) has one optional parameter and two overloads. If we have two optional parameters, there are four overloads:
  1. public void DoSomethingElse(string s, decimal d)  
  2. {  
  3.         Console.WriteLine( "{0}, {1}" , s, d);  
  4. }  
  5.    
  6. public void DoSomethingElse(string s)  
  7. {  
  8.         DoSomething(s, 5);  
  9. }  
  10.    
  11. public void DoSomethingElse(decimal d)  
  12. {  
  13.         DoSomething("Hello", d);  
  14. }  
  15.    
  16. public void DoSomethingElse( )  
  17. {  
  18.         DoSomething("Hello", 5);  
  19. }  
This can get out of hand quickly. It's also error prone.
 

Optional Parameters

 
Optional parameters make life easier. Here is one function to replace the four overloaded methods shown above:
  1. public void DoSomethingElse(string s = "Hello"decimal d = 5)  
  2. {  
  3.         Console.WriteLine("{0}, {1}", s, d);  
  4. }  
There are a few restrictions.
  1. All optional parameters must come after the required parameters. So this is invalid:
    1. // Invalid:  optional parameter before required parameter  
    2. public void DoSomething(string s = "Hello"decimal d)  
    3. {  
    4.       ...  
    5. }
  2. The default value for an optional parameter must be a constant. It can be a string or a number, const, null, or an enum.
    1. // Invalid:  optional parameter is not a constant.  
    2. public void DoSomething(DateTime dt = DateTime.Now)  
    3. {  
    4.       ...  
    5. }  
    One possible way to overcome this restriction is as follows:
    1. // Setting a default value of DateTime.Now  
    2. public void DoSomething(Nullable<DateTime> dt = null)  
    3. {  
    4.         if (!dt.HasValue)  
    5.                dt = DateTime.Now;  
    6. }

Optional Parameters With Named Arguments

 
One case where optional parameters run into trouble is when there is more than one optional parameter with the same data type. For example:
  1. public void DoSomething(int a = 5, int b = 10)  
  2. {  
  3.     ...  
  4. }  
In this case, it's not clear what a call to DoSomething(37) means. Are you passing in a or b? This is where named arguments come in handy. Named parameters (also new to .NET 4.0) allow you to specify which parameter you intended.
  1. // a = 37.  b = the default value.  
  2. DoSomething(a: 37);  
  3.    
  4. // a = the default value.  b = 37.  
  5. DoSomething(y: 37);  
Beware
 
Using optional parameters in class libraries can cause unexpected problems. Suppose I have a class library that contains the following method:
  1. public int DoSomething( int x = 30)  
  2. {  
  3.     return x;  
  4. }  
I wrote a program that uses the class library and calls DoSomething( ). DoSomething returns the default value, 30.
 
Now suppose I modify the class library and change the default value:
  1. public int DoSomething( int x = 40 )  
  2. {  
  3.     return x;  
  4. }  
I recompile the class library and then run my program again. I call DoSomething( ) and it still returns 30. 
 
The only way I can get the correct result is if I recompile my program as well.