Named arguments, optional arguments, and default values

C# 4.0 introduces the concept of optional parameter values into the language. Now, this has been a controversial subject in the past, and we have had many requests for the feature, but have traditionally stayed away from it. So, why now?

Well, before we get into the philosophy of why we decided to add it this time (which we will! I promise!), first lets discuss the feature itself.

I'll first let's talk about the features themselves, and will follow that with a brief discussion about the focus of C# 4.0 and how these features align with that focus. I'll expand in depth as to how each feature works, and what the compiler does behind the scenes in future posts.

Default parameter values

We've all had the experience of writing (or at least using) methods whose parameters are optional in nature. Often, those methods will contain overloads which will have the optional parameters removed, and will simply be a wrapper for the actual method, passing the default values that the library writer wants to provide for the method.

Default parameter values give us a way to explicitly declare the default value that we would like to be used for the parameter in the method signature itself, instead of in the forwarding method. This gives the added benefit of allowing the IDE to help out and inform the consumer of the default value for the given parameter.

These guys are specified in one of two ways:

 public class C
    static void Foo([DefaultParameterValueAttribute(10)] int x, int y = 20) { }

The first mechanism is purely a backwards compatibility issue and an interop issue with COM and VB. We highly recommend against using this method, however, because it specifies that there is a default parameter value for the parameter, but does not specify that the parameter is in fact optional. This means that the default value given will never be used.

The second mechanism really says two things - first, it tells the compiler that this parameter is optional, meaning the user does not have to specify an argument in this position. Second, it gives the compiler the value to use when the user does not specify an argument for the parameter.

How default parameter values are encoded

Because the new syntax really does two things, it is encoded in the metadata as two things. We encode both the optional flag in the metadata signature (which is equivalent to using the OptionalAttribute attribute), and we encode the default value as the DefaultParmeterValueAttribute attribute.

These two mechanisms have already existed in the CLR, and are in fact what the VB compiler produces today, giving us an easy choice for how to encode the feature.

Note that the compiler will give you an error if you try to specify both a DefaultParameterValueAttribute attribute as well as a default value using the new syntax.

Also note that the compiler enforces that all optional parameters using the new syntax are specified after all required parameters.

Lastly, the compiler will not allow you to specify default parameter values for ref or out parameters. This is because there is no valid constant expression that is convertible to the ref or out type.

Optional arguments - how default parameter values are used

In order to make use of the default parameter values, we've added a feature which allows the caller to omit arguments for parameters which have default parameter values specified.

Notice that because we enforce that optional parameter values are specified at the end of the parameter list, you cannot omit an argument but specify an argument for a later position.

This means that even though existing libraries may have specified the DefaultParameterValueAttribute and the OptionalAttribute for a parameter in the middle of the list, the C# compiler will not allow you to call that method without specifying a value for that parameter.

You can kind of think of optional arguments as a params array - the compiler will allow you to call the method without specifying the arguments, but they must be at the end of the parameter list.

What gets code gen'ed?

First we need to note that the use of optional arguments is really just syntactic sugar. The compiler cannot generate a call without actually providing all the arguments - it simply provides an argument for you and allows you to omit specifying it.

Once the compiler realizes that you're calling a method and omitting an argument because it is optional, it takes the value specified in the DefaultParameterValueAttribute and encodes that as a constant value for the argument that you've omitted. Note that if you're calling a library that doesn't have a default value specified but is still optional, the compiler will use default(T) as the value, where T is the type of the parameter. Also note that for COM calls, we also allow you to omit arguments to ref parameters by generating the temporaries for you.

Putting it all together - Named arguments

The ability to omit specifying arguments for parameters that have a default value is really taken advantage of by the Named arguments feature. This feature allows you to specify by name the parameter for which you are providing a value. This means that you can now omit specifying arguments for all parameters, and only specify the ones which you actually care about.

For COM programmers, this is like heaven! I recently "got" to play with Office interop a bit, and found that the average method had 30 parameters! Most of the parameters are wonderfully optional, and with the ability to now omit them, code looks much cleaner and is much more readable.

Sorry, I'm getting ahead of myself. First let me describe the feature.

Named argument usage

This feature introduces new syntax at the call site of a method. Consider the following example:

 public class ContactList
    List<Contact> SearchForContacts(
        string name = "any",
        int age = -1,
        string address = "any") { ... }

    static void Main()
        ContactList list = new ContactList();
        var x = list.SearchForContacts(age:26);

We've got some library method that searches through some contacts and finds the ones that match our criteria (I know, its a horrible API, but bear with me). The library is great! ;) It specifies the default wildcards for us so that we can omit them as we wish. Now suppose we want to find all contacts aged 26.

Well, since we now have optional arguments, we can omit the arguments after the age parameter, but that gets us half way there. Named arguments get us the rest of the way.

By using the new syntax, we can specify by name the argument that we want to specify a value for, and omit the rest. The syntax is quite simple (well, not really, but I'll get into the details of its complexities later): simply specify the name of the parameter that you want to specify an argument for, add a colon, then add the value that you want.

Note that the value can be any expression type that you normally could have specified. Note also that named arguments are not restricted to parameters that are optional or have default values. Lastly, note that you don't even have to specify the arguments in order! We could call our method with the following:

 list.SearchForContacts(address:"home", name:"sam", age:30);

The arguments don't have to be in any particular order. The only rule is that the named arguments must be specified at the end of the argument list. This means that all positional arguments (the "normal" ones that aren't specified by name) must be given first. Once the compiler encounters a named specification, it will produce an error upon encountering any further unnamed arguments.

So that's the feature in a nutshell. There are more details that I'll get to later, but in the mean time I'd love to get your feedback on the feature, on its uses, and on how we've chosen to design it.

And as always, happy coding!

kick it on