Named arguments, optional arguments, and default values

Named arguments, optional arguments, and default values

Rate This
  • Comments 30

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 DotNetKicks.com
Leave a Comment
  • Please add 3 and 2 and type the answer here:
  • Post
  • You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Good enhancements. Named arguments to me can also possibly make program more readable by making them as part of regular coding guidelines. Reading a name (a meaningful name offcourse) along with the value in a function call will enable quick understanding of what argument was expected and what is it that we are passing.

    So i guess this means better maintenance

  • Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Why not allow parameter values that come from instantiation or method calls? You could use code generation to create the overload.

  • I'm very curious about the decision to resolve overload and parameter values at compile time. Wouldn't deferring some of this until run-time give library authors more flexibility in maintaining backwards-compatibility between versions?

  • Atul:

    Exactly! No more things like Print(false /*printHeaderInfo*/). Now we can do Print(printHeaderInfo:false);

    von-Kloeten:

    The problem there is that there is no way to specify in the attribute that we want to have a method call. Attribute arguments must be constants, and we've got to be back-compat with the current architecture that VB already has in place.

    apinzur:

    Again, this is based on being compatible with the current architecture that's already in place. In the same vein, you could argue that we should do all of overload resolution at runtime, so that the library implementer could provide more specific methods that will be called. We've already got a feature for that though - dynamic! :)

  • Shouldn't that be list.SearchForContacts(address:"home", name:"sam", age:30); ?

  • Great catch Ziegler! I've updated the entry to fix that. Thanks!

  • One interesting side-effect of this is that it can also make it much easier to write immutable types, without requiring vast quantities of constructor overloads to cover different use-cases (and also enabling something like object initializer syntax).

    http://marcgravell.blogspot.com/2008/11/immutability-and-optional-parameters.html

  • C# is becoming more VB-like all the time.  Optional arguments have been part of VB for, what, 10+ years?  It's been one of the arguments VB programmers have for why VB is better than C# (late binding is another that I also see is now becoming part of C# - dynamics).  Haven't we been told that it's better to write explicit overrides?  So why the switch now?  How long till the only difference between C# and VB is the semicolons at the end of the lines?  Not that I see it as a bad thing - just makes choice of language more of a moot point.

  • As every new version of C# comes out, I wonder how I ever lived in the dark ages prior to C# v(current version here).

  • I have a question here:

    Let us say there is a typo in the "SearchForContacts" method's "name" parameter and the programmer mistyped it as "mane" (instead of "name") and say the library is released. As a client of that library I coded in my program such that "SearchForContacts(mane:"satya");

    In the later versions the parameter name is corrected (from "mane" to "name") and a new version of the library is released.

    Now I think my client doesn't work with the new library. Am I correct here?

  • Optional and named parameters特性在有些场合提供很大的方便,特别是Office开发中可以告别一坨System.Reflection.Missing了。这里简单了解一下C#4.0中的Optional and named parameters。

  • Marc.gravell - You're entirely correct with that thought. We've been giving immutable types a lot of thought, and while we haven't decided anything in terms of the language, we definitely have thought a lot about features which would help achieve this goal.

    Widdowsj - You're right in saying that we are co-evolving with VB. You should check out Anders' talk at PDC about the Coevolution effort - he speaks more to the topic. Why the switch now - this release of C# focuses on interop, both between languages and platforms. COM interop is particularly a focus that we are taking this time around, and named arguments are a great asset to that technology.

    I believe that in the beginning, we believed COM would disappear with the introduction of .NET. Its been quite a few years since, and COM is still as widely used as ever. So we decided, if y'cant beat em, join em :)

    Bulusu - no, the client will work with the new library. The C# compiler does the reordering of arguments at compile time. This means that the IL generated looks as though you simply passed the parameter positionally. For example, suppose I have a method Foo(int x, int y) and I call it with Foo(y:10, x:20). The reordering of the names is compiler sugar - the IL generated looks as though you were calling Foo(20,10) instead of Foo(y:10, x:20).

  • samng,

    I am not sure I clerarly explained or not, but I am talking about the SPELLING of the parameter names in the prototype, not the ordering.

    For example:

    In version-1 of my Libray I have a method as follows:

    void foo(int x=10);

    Let us say I have a client calling as follows:

    foo(x:20);

    Now in version-2 od my Libray the method is changed as follows:

    void foo(int y=10);

    But my client is not changed so it is still calling:

    foo(x:20);

    I am just wondering it would casue any issues.

    As you know normally in any prgramming lanugage (as far as I know) we won't depend upon the names of the calling method's parameters. But with C# 4.0 we can and I was wondering that could cause any incompatibilities.

    Hope I explained clearly this time.

    Thanks,

    Satya Bulusu

Page 1 of 2 (30 items) 12