Saturday, November 01, 2008 10:58 PM bart

C# 4.0 Feature Focus - Part 2 - Named parameters

In the previous episode of this feature focus series we talked about optional parameters. Today we'll cover another feature introduced in C# 4.0, named parameters. One of the most applicable places for optional and named parameters is when dealing with COM interop such as interaction with the Office automation APIs, but they can also be used as a stand-alone language feature. Just like optional parameters, named parameters are a symmetric feature: you can both consume and declare them.

 

The syntax

Assume the following simple subtraction method is defined:

static int Subtract(int a, int b)
{
    return a - b;
}

The typical way to call this method is obviously by specifying the parameters in order, like Subtract(5, 3). However, with named parameters it's possible to write the following:

static void Main()
{
    Console.WriteLine(Subtract(b: 3, a: 5));
}

Ultimately this translates into a call to Subtract(5, 3). It's clear the sample above is not very realistic (but you have to admit it's simplistic). Typically named parameters are used where optional parameters appear in the target method, but you're not interested in lots of those:

static void Bar(int a = 1, string b = null, bool c = false)
{
    // ...
}

Now assume you're only interested in the last parameter, without the named parameter feature you'd have to write Bar(1, null, ...) but now you can go ahead and write:

Bar(c: true);

You might wonder why the syntax uses a colon (:) instead of an assignment equals character (=). The answer is straightforward: assignments have a value and can be used everywhere a value is expected:

bool c = false;
Bar(c = true);

This will assign true to the local variable c and feed that value in as the first argument of Bar. So colon is the way to go (and I'm not going to start religious debates about the spacing around the :, I'll leave that to the C/C++ community as those people are experienced in this kind of discussions, trying to reach an agreement on the totally irrelevant placing for the * character <g>).

 

The implementation

It should be clear that the implementation only affects the call site, not the caller. Here's how the Main method from above looks like:

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       19 (0x13)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  stloc.0
  IL_0003:  ldc.i4.5
  IL_0004:  stloc.1
  IL_0005:  ldloc.1
  IL_0006:  ldloc.0
  IL_0007:  call       int32 Program::Subtract(int32,
                                               int32)
  IL_000c:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0011:  nop
  IL_0012:  ret
} // end of method Program::Main

First of all, notice the names of parameters don't appear in the call site in any way (they never have, that's not the way IL works). Ultimately we simply call Subtract with the parameters supplied in the right order. But how we get there is important to take a closer look at:

  IL_0001:  ldc.i4.3
  IL_0002:  stloc.0
  IL_0003: 
ldc.i4.5
  IL_0004:  stloc.1
  IL_0005: 
ldloc.1
  IL_0006:  ldloc.0

The thing to notice here are the mirrored stloc (store to local variable) versus ldloc (load from local variable) instructions. On lines IL_0002 and IL_0004 values are stored to variables 0 and 1, while on lines IL_0005 and IL_0006 they're read out in reverse order. What's happening here is that arguments to the method call are evaluated in lexical order, something that boils down to a one-liner in section 14.4.1 of the C# specification (ECMA-334):

14.4.1    Argument lists
...
The expressions of an argument list are always evaluated in the order they are written.

[Example: Thus, the example

class Test
{
  static void F(int x, int y, int z) {
    System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
  }

  static void Main() {
    int i = 0;
    F(i++, i++, i++);
  }
}

produces the output

x = 0, y = 1, z = 2

end example]

This becomes relevant in the context of side-effects but it should be clear by now that it's better not to rely on those kind of side-effects at all. Nevertheless, consistency is a must and hence the named parameters invocation syntax follows those rules as well. Here's the new sample you can predict the output for based on the previous observations:

class Test
{
  static void F(int x, int y, int z) {
    System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
  }

  static void Main() {
    int i = 0;
    F(z: i++, x: i++, y: i++);
  }
}

 

The caveat

This time the caveat is trivial: don't rename parameters on public methods as they might be used in conjunction with the named parameter feature. Here's a sample.

Step 1: Compile the following (csc /t:library namedlib.cs)

public static class NamedLib
{
    public static int Subtract(int a, int b)
    {
        return a - b;
    }
}

Step 2: Compile the following (csc named.cs /r:namedlib.dll)

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine(NamedLib.Subtract(b: 3, a: 5));
    }
}

Step 3: Run named.exe

> named.exe
2

Step 4: Change the library and recompile

public static class NamedLib
{
    public static int Subtract(int x, int y)
    {
        return x - y;
    }
}

Step 5: Recompile the application

named.cs(7,27): error CS1739: The best overload for 'Subtract' does not have a parameter named 'b'

Names matter. Obviously reordering parameters of the same type without recompiling consumers is a breaking change too (the types of the parameters in the signature doesn't change, so the overload is still valid but semantics of the parameters have changed), but that's unrelated to the use of named parameters.

 

Conclusion

Named parameters provide an easy way to omit ("skip over") optional parameters and are typically used in conjunction with that particular feature. However, they can be used in isolation as well. Once more, library designers should be cautious about messing with the public methods they provide. In particular, make sure names on publicly exposed methods are stable as changes to those have the potential of breaking callers. Another reason to spend time on XML documenting public members, as you'll double-check (I hope) the <param /> tag for its name.

Oh, and did you notice the parallels with concepts in PowerShell on positional and named cmdlet arguments? The only piece missing are aliases ;-).

Next time, before we dive into more language-specific features, a short intermezzo in LINQ with a new operator: Zip. Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# re: C# 4.0 Feature Focus - Part 2 - Named parameters

Sunday, November 02, 2008 8:47 AM by Olmo

Pleeease?.Tell?.Me?.That?.This?.Is?.The?.New?.Operator();

# C#4.0新特性之二:Named and Optional Parameters (2) 剖析篇

Sunday, November 02, 2008 11:15 AM by new 维生素C.net()

在上一篇介绍该特性的文章里,大家都在说这是vb里已经有的东西.切不管c#的发展方向,我们剖析一下这个特性的实现原理:

首先,还是看一个例子:

Codehighlightingproduc...

# Reflective Perspective - Chris Alcock &raquo; The Morning Brew #214

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #214

# re: C# 4.0 Feature Focus - Part 2 - Named parameters

Monday, November 03, 2008 12:49 AM by Anders Dalvander

As named parameters only affect the call site, will you get a compiler warning/error when you change the names of parameters in derived classes?

class Base

{

  public virtual void Foo(int a, int b)

  {

  }

}

class Derived : Base

{

  public override void Foo(int b, int a) // compiler warning/error?

  {

  }

}

# Dew Drop - November 3, 2008 | Alvin Ashcraft's Morning Dew

Monday, November 03, 2008 5:39 AM by Dew Drop - November 3, 2008 | Alvin Ashcraft's Morning Dew

Pingback from  Dew Drop - November 3, 2008 | Alvin Ashcraft's Morning Dew

# re: C# 4.0 Feature Focus - Part 2 - Named parameters

Monday, November 03, 2008 4:09 PM by akx

Hello Bart, thanks for insightful post, my question is -- if it's so dangerous (and I think it is, I like to refactor my code and during that many strange things can happen, renaming of parameters and variables happen very often), do I have an option to stop users of my class from calling my methods using named parameters? Can it be switched off by a class/method attribute, compiler option or project property?

Thank you.

# re: C# 4.0 Feature Focus - Part 2 - Named parameters

Monday, November 03, 2008 7:03 PM by bart

Hi akx,

The short answer is no. In fact, you're already "vulnerable" to this kind of problems as people using VB might be calling your code using named parameters:

NamedLib.Subtract(a := 2, b := 1)

It's unfortunate named and optional parameters are not regulated by the CLS (whether it's a CLS thing or not is debatable, as this is primarily a versioning-related matter). This said, when thinking about publicly visible types, every modification to existing publicly visible members should be considered breaking if not proven otherwise. The caveats outlined in this post definitely fall under the umbrella of breaking changes.

Having an attribute somehow to restrict uses would work if we wouldn't have to consider all other languages on the CLR that might or might not have a named parameter feature. Adding such a thing after the fact is virtually impossible, and again we end up in the terrain of the CLS as such a mechanism would need to be part of the cross-language contract.

Hope this helps,

-Bart

# C# 4.0 Feature Focus - Part 3 - Intermezzo: LINQ's new Zip operator

Monday, November 03, 2008 11:11 PM by B# .NET Blog

After named parameters and optional parameters , we&#39;ll take a little breadth and deviate a bit from

# Silverlight 3.0 beta

Wednesday, April 08, 2009 5:46 AM by sephiroth.it - flash world

Today I discovered this page about the upcoming Silverlight 3.0 and I was really surprised about the ton of new features they are adding in this new release. In fact, reading at the features list it confusing me… it seems...

# Websites tagged "cil" on Postsaver

Monday, May 04, 2009 1:32 PM by Websites tagged "cil" on Postsaver

Pingback from  Websites tagged "cil" on Postsaver

# Richard Dingwall &raquo; Fluent Builder Pattern for classes with long-ish constructors

Pingback from  Richard Dingwall  &raquo; Fluent Builder Pattern for classes with long-ish constructors

# Silverlight 3.0 beta | sephiroth.it

Thursday, June 10, 2010 3:17 PM by Silverlight 3.0 beta | sephiroth.it

Pingback from  Silverlight 3.0 beta | sephiroth.it