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 8 and 5 and type the answer here:
  • Post
  • Hi Satya,

    My apologies that my response was not clear. I understand the scenario you're describing, and my response speaks to that scenario.

    In your client code, you call foo(x:20), but the IL that gets generated for this is foo(20). Notice that the IL has no trace of the name "x", nor does it have any trace of the named orderings that may be in your call site.

    Even though the v2 of your library changes the name, it is irrelevant - the IL doesn't reflect the name at all.

    The names are only used as compile time sugars to tell the compiler explicitly which argument value is being specified. The compiler takes this information, uses it, and discards all traces of it in the IL stream generated.

    Hope that's more clear!

    - Sam

  • Sam,

    Thanks for the explanation. I am not sure how much you are bored with this blog :) but here is another case I am wondering:

    Version-1 of my library:

    ================

    void foo(int x=0, int y=0);

    Client for the library:

    ==============                          

    foo(y:20, x:10); ==C# Compiler==> foo(10,20) (Correct me if I am wrong)

    Version-2 of my library:

    =================

    void foo(int a=0, int b=0);

    Client for the library(not changed):

    ========================

    foo(y:20, x:10);==> ? How does the compiler knows to map y for b and x for a?

    Am I missing any important point here?

    Hopefully answer to this question will stop me bothering you more on this blog :)

    Thanks,

    Satya

  • Satya,

    When you say "Client for the library not changed", do you mean that you are not recompiling it? Or are you simply saying you're not changing the source, but you ARE recompiling? If you are recompiling, then the compiler will give you an error saying that the name does not exist.

    Let me illustrate the steps:

    1) Compile version 1 of your library:

    void Foo(int a = 0, int b = 0);

    2) Compile the client code:

    Foo(b:20, a:10);

    3) Compile version 2 of your library:

    void Foo(int x = 10, int y = 20);

    4) Recompile your client code:

    Foo(b:20, a:10);

    Step 4 generates a compiler error.

    Hope that helps!

  • Nice feature. A useful addition without being a pedagogical nightmare.

  • 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

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

  • 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

  • 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

  • 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

  • Looks like Objective-C inspiration to me.

  • 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

  • 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.

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

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

  • ........nice work....

    www.buddycode.in

Page 2 of 2 (30 items) 12