As part of the co-evolution effort of C# and Visual Basic, C# 4.0 introduces Named and Optional Arguments. First of all, let’s clarify what are arguments and parameters: Method definition parameters are the input variables of the method. Method call arguments are the values provided to the method parameters. In fact, the C# Language Specification states the following on §7.5: The argument list (§7.5.1) of a function member invocation provides actual values or variable references for the parameters of the function member. Given the above definitions, we can state that: Parameters have always been named and still are. Parameters have never been optional and still aren’t. Named Arguments Until now, the way the C# compiler matched method call definition arguments with method parameters was by position. The first argument provides the value for the first parameter, the second argument provides the value for the second parameter, and so on and so on, regardless of the name of the parameters. If a parameter was missing a corresponding argument to provide its value, the compiler would emit a compilation error. For this call: Greeting("Mr.", "Morgado", 42);
this method:
public void Greeting(string title, string name, int age)
will receive as parameters:
title: “Mr.”
name: “Morgado”
age: 42
What this new feature allows is to use the names of the parameters to identify the corresponding arguments in the form: name:value
Not all arguments in the argument list must be named. However, all named arguments must be at the end of the argument list. The matching between arguments (and the evaluation of its value) and parameters will be done first by name for the named arguments and than by position for the unnamed arguments.
This means that, for this method definition:
public static void Method(int first, int second, int third)
this call declaration:
int i = 0;
Method(i, third: i++, second: ++i);
will have this code generated by the compiler:
int i = 0;
int CS$0$0000 = i++;
int CS$0$0001 = ++i;
Method(i, CS$0$0001, CS$0$0000);
which will give the method the following parameter values:
first: 2
second: 2
third: 0
Notice the variable names. Although invalid being invalid C# identifiers, they are valid .NET identifiers and thus avoiding collision between user written and compiler generated code.
Besides allowing to re-order of the argument list, this feature is very useful for auto-documenting the code, for example, when the argument list is very long or not clear, from the call site, what the arguments are.
Optional Arguments
Parameters can now have default values:
public static void Method(int first, int second = 2, int third = 3)
Parameters with default values must be the last in the parameter list and its value is used as the value of the parameter if the corresponding argument is missing from the method call declaration.
For this call declaration:
int i = 0;
Method(i, third: ++i);
will have this code generated by the compiler:
int i = 0;
int CS$0$0000 = ++i;
Method(i, 2, CS$0$0000);
which will give the method the following parameter values:
first: 1
second: 2
third: 1
Because, when method parameters have default values, arguments can be omitted from the call declaration, this might seem like method overloading or a good replacement for it, but it isn’t.
Although methods like this:
public static StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = true,
int bufferSize = 1024)
allow to have its calls written like this:
OpenTextFile("foo.txt", Encoding.UTF8);
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);
OpenTextFile(
bufferSize: 4096,
path: "foo.txt",
detectEncoding: false);
The complier handles default values like constant fields taking the value and useing it instead of a reference to the value. So, like with constant fields, methods with parameters with default values are exposed publicly (and remember that internal members might be publicly accessible – InternalsVisibleToAttribute). If such methods are publicly accessible and used by another assembly, those values will be hard coded in the calling code and, if the called assembly has its default values changed, they won’t be assumed by already compiled code.
At the first glance, I though that using optional arguments for “bad” written code was great, but the ability to write code like that was just pure evil. But than I realized that, since I use private constant fields, it’s OK to use default parameter values on privately accessed methods.