다음을 통해 공유


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 DotNetKicks.com

Comments

  • Anonymous
    February 03, 2009
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    February 03, 2009
    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

  • Anonymous
    February 03, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

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

  • Anonymous
    February 04, 2009
    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?

  • Anonymous
    February 04, 2009
    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! :)

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

  • Anonymous
    February 18, 2009
    Great catch Ziegler! I've updated the entry to fix that. Thanks!

  • Anonymous
    February 25, 2009
    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

  • Anonymous
    February 26, 2009
    The comment has been removed

  • Anonymous
    March 06, 2009
    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).

  • Anonymous
    March 07, 2009
    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?

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

  • Anonymous
    March 10, 2009
    The comment has been removed

  • Anonymous
    March 10, 2009
    The comment has been removed

  • Anonymous
    March 10, 2009
    The comment has been removed

  • Anonymous
    March 11, 2009
    The comment has been removed

  • Anonymous
    March 11, 2009
    The comment has been removed

  • Anonymous
    March 11, 2009
    Nice feature. A useful addition without being a pedagogical nightmare.

  • Anonymous
    March 12, 2009
    Sam, Yes, I am wondering about the scenario where a client is not recompiled, which I think is pretty understandable becasue the client might expect that a newer version of a library keeps its public method interfaces intact. But my dilemma here is that the new feature of C# might cuause clinets to throw Exceptions (MethodNotFound or some thing) and of cource eventual recompilation after fixing the prameter names might be able to let clients to work with the new version of the library where a method's parameter names are changed. Hope I am explained my question clearly. Thanks, Satya

  • Anonymous
    March 13, 2009
    Apple&#39;s OS X and iPhone language du jour Objective-C contains both archaisms and a number of excellent

  • Anonymous
    March 19, 2009
    Satya - the new feature is really not causing any additional issues. The problem you describe is with the library itself. Named arguments make the name, not order, of the argument important, even if the feature's syntactic sugar makes it APPEAR that parameter ordering is important. Thus, if you want to use named values in a library, you should not remove/change the names or else clients will break. This exactly mirrors the original way of resolving method signatures -- you should not remove/change the ordering of the types or else clients will break. The only confusion is that the syntax of named arguments make it APPEAR that ordering of types is important when it is not, and that seems acceptable. Changing external interface of a Library should not be done lightly. //David

  • Anonymous
    March 20, 2009
    David, That is exactly what my point is. Implementing named arguments feature in a library will NOT let the library developers to rename the names of the library method arguments in the later releases as it could break clients using them in named arguments fashion. One might argue that "Named arguments are like method names in a library  and changing them would break clients just like changing the method names would break. So what is the big deal". My point is,  yes it might not be a big deal but this is new to programmers of C/C++/Java/C# (unitl now) who unitl now are careless about the argument names in a method's signature that they call from their client. Now my understanding is they can't be careless anymore. Please correct me if I am wrong. --- Satya

  • Anonymous
    March 23, 2009
    Satya, Using named arguments WILL let the library developers rename the names of library methods in later releases, and WILL NOT break old clients binding against the library, as long as the client is not recompiled. As David suggests, the feature is syntactic sugar that tells the compiler how to rearrange your arguments. When you compile your client, it loses all notion of named arguments. Suppose my library method is: void MyLibraryMethod(string mane, int age){}. Without named arguments, you would simply call the method as follows: MyLibraryMethod("Sam", 10); However, with named arguments, you can now call it like so: MyLibraryMethod(age:10, mane:"Sam"); Now when you compile this, the compiler removes the fact that you gave a named specifier, and simply generates code for the following: MyLibraryMethod("Sam", 10); This means that changing the library method's parameter name from "mane" back to "name" will have no effect on the client. Hope that helps,

  • Sam
  • Anonymous
    March 25, 2009
    Looks like Objective-C inspiration to me.

  • Anonymous
    March 27, 2009
    widdowsj wrote> "the only difference between C# and VB is the semicolons at the end of the lines? "... I don't agree with the changes implemented in C#... sincerly.. each new version seems like VB more... what is the point?.. do wyou want that  VB programmers change to C# without says "nooooo" ???.. Implementing this similar features or codifying sintax.. the next step is drop VB and point to C# only... anyway.. : writeline("C#" == "VB" ? "Yes" : "Yes"); LOL

  • Anonymous
    March 31, 2009
    Renaming your arguments in non-private/internal methods was always a bad idea, and they were always part of your public contract (with all the burden of versioning that comes with that) because any C# code you write may be used by client VB code (or other .NET language which has named arguments), so you will break things for them by renaming (not for compiled modules, but when recompiling, they will break). This has been true since the very first .NET release. It surprises me that C# developers only now realize that it is the case.

  • Anonymous
    April 01, 2009
    Last time we talked about the basics of named arguments, optional arguments, and default values . From

  • Anonymous
    April 17, 2009
    Okay, my attempt at a clever title failed… Ties and Philosophers? I oughtta stick with technical writing.

  • Anonymous
    February 23, 2014
    ........nice work.... www.buddycode.in