Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

C# 4.0 – Named and Optional Parameters – Behind the Scenes

In my previous post, I talked very briefly about both named and option parameters.  Now, I’d like to go a little deeper into the subject to see what this looks like underneath the covers.  As before, we’ll look at the limitations as well that we faced in the previous post. 

 

Optional Parameters

First, let’s define our class that will contain our optional parameters.  This will be a reuse of our class that we defined in the last post of the TextBoxInfo class.  We will define a constructor, just for example sake, with all parameters being optional.  Not that I’d recommend that, but let’s just at least look at the possibility. 

public class TextBoxInfo
{
    private readonly string text;
    private readonly float size;
    private readonly float width;
    private readonly Color color;

    public TextBoxInfo(
        string text = "",
        float size = 10.0f,
        float width = 50.0f,
        Color color = new Color())
    {
        this.text = text;
        this.size = size;
        this.width = width;
        this.color = color;
    }

    public string Text { get { return text; } }
    public float Size { get { return size; } }
    public float Width { get { return width; } }
    public Color TextColor { get { return color; } }
}

 

The real interesting part of this code will be in the how the constructor looks.  This is where we’ll be setting the default values.  Let’s look through .NET Reflector to see the generated code for the constructor.

public TextBoxInfo(
  [Optional, DefaultParameterValue("")] string text, 
  [Optional, DefaultParameterValue(10f)] float size, 
  [Optional, DefaultParameterValue(50f)] float width, 
  [Optional] Color color)
{
    this.text = text;
    this.size = size;
    this.width = width;
    this.color = color;
}
 

What’s interesting to note is the use of the System.Runtime.InteropServices.OptionalAttribute and the System.Runtime.InteropServices.DefaultParameterValueAttribute.  The OptionalAttribute is used to specify that the parameter is optional, and the DefaultParameterValueAttribute specifies the value should the value not be specified.  Given that our text, size and width are all primitives, we can use the DefaultParameterValueAttribute to specify the default value.  For our color parameter, since it’s a complex type, its value cannot be set through optional parameters, therefore, its default value is specified by the Color default constructor.

Let’s compare that implementation to the F# implementation. 

type TextBoxInfo(?text:string
                 ?size:float
                 ?width:float
                 ?color:Color)
  let text = defaultArg text ""
  let size = defaultArg size 10.
  let width = defaultArg width 50.
  let color = 
    match color with 
    | None -> Color.Red 
    | Some c -> c  
     
  member x.Text = text 
  member x.FontColor = color 
  member x.Width = width
  member x.Size = size
 

Once again, let’s turn our eyes to the constructor details of the F# implementation.  Opening up Reflector, we’ll take a look at how default values are handled via the constructor.  How does this differ from the C# implementation?

public TextBoxInfo(
  [OptionalArgument] Option<string> text, 
  [OptionalArgument] Option<double> size, 
  [OptionalArgument] Option<double> width, 
  [OptionalArgument] Option<Color> color)
{
    Program.TextBoxInfo @this = this;
    @this.text@5 = text;
    @this.size@5 = size;
    @this.width@5 = width;
    @this.color@5 = color;
    @this.text@7 = Operators.defaultArg<string>(@this.text@5, "");
    @this.size@8 = Operators.defaultArg<double>(@this.size@5, 10.0);
    @this.width@9 = Operators.defaultArg<double>(@this.width@5, 50.0);
    Option<Color> option = @this.color@5;
    @this.color@10 = (option == null) ? Color.Red : option._Some1;
}

From the above syntax, you will notice the use of the defaultArg function which sets the value of our member values on those which are simple types.  For more complex situations, we can then use pattern matching to define the default value of our color value.  This is a nice implementation as it allows me to initialize complex types in a rather controlled way as well as the simple types.

One more example I want to look at is the indexed properties and what they look like.  Let’s take an example of a wrapper for the much maligned DataRow object.  I’ll add values for the column name, and optionally the DataRowVersion enum.  The code to wrap should look much like this:

public object this[string column, DataRowVersion version = DataRowVersion.Current]
{
    get
    {
        return this.row[column, version];
    }

Popping this open in Reflector once again shows us that the DefaultParameterValueAttribute is set to the DataRowVersion.Current value, which in this case is 0x200.  As enums aren’t strictly typed as they are in Java, the C# compiler can then turn the enum value into a primitive.

public object get_Item(
  string column, 
  [Optional, DefaultParameterValue(0x200)] DataRowVersion version)
{
    return this.row[column, version];
}

Having looked this over, let’s move onto the named parameters implementation.

 

Named Parameters

Named parameters gives us the option of specifying the parameters by name, rather than by absolute position.  This is quite handy when dealing with a method signature with many parameters, and especially ones not needed.  You simply need to look at the managed Microsoft Office Libraries for any further proof of that.  But, you don’t always have to use named parameters when there are optional parameters.  Instead, you can use them pretty much any time you like.  Let’s take our above TextBoxInfo class and create three instances, and each one will set a different combination of parameters. 

static void Main(string[] args)
{
    var t1 = new TextBoxInfo(
        text: "Hello World"
        size : 12.0f);
    var t2 = new TextBoxInfo(
        size : 10.0f
        width : 45.0f);
    var t3 = new TextBoxInfo(
        text : "Hi there",
        width : 50.0f);

    Console.WriteLine(t1.Width);
    Console.WriteLine(t2.Text);
    Console.WriteLine(t3.Size);
}

 

What I’m writing out is the default values for each of the parameters I didn’t set.  Popping up Reflector once again, let’s look at the generated code.

private static void Main(string[] args)
{
    string CS$0$0000 = "Hello World";
    float CS$0$0001 = 12f;
    TextBoxInfo t1 = new TextBoxInfo(CS$0$0000, CS$0$0001, 50f);
    CS$0$0001 = 10f;
    float CS$0$0002 = 45f;
    TextBoxInfo t2 = new TextBoxInfo("", CS$0$0001, CS$0$0002);
    CS$0$0000 = "Hi there";
    CS$0$0001 = 50f;
    TextBoxInfo t3 = new TextBoxInfo(CS$0$0000, 10f, CS$0$0001);
    Console.WriteLine(t1.Width);
    Console.WriteLine(t2.Text);
    Console.WriteLine(t3.Size);
}

What the compiler is doing behind the scenes is creating inline instances of our named parameters and feeding it to the constructor.  As you will notice, it’s feeding the named instances as well as putting in the default value that we specified in the method signature.  Nothing really special about it.  Very interesting to see how some of these challenges are handled underneath the covers.

 

Wrapping it Up

As you can see, named and optional parameters can be very handy to use, whether in some connected fashion or not.  We can use these in combination to deal with other languages and libraries in a rather flexible fashion.  It’s about time C# 4.0 caught up in this regard.  I have a wish list continued for C# 5.0 which still includes syntactic sugar for allowing for mixins, discriminated unions, tuples, pattern matching, immutable types, and so on.  The list could go on for a while…  As I said before, download the Visual Studio 2010 and .NET Framework 4.0 CTP and give it a spin.

Also, be sure to catch the C# 4.0 team on a podcast with Mads Torgersen, Anders Hejlsberg and Eric Lippert:

This entry was posted in C#, F#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Gaurav
  • http://podwysocki.codebetter.com Matthew.Podwysocki

    @Felipe

    Yes, this could be an issue if you change your mind what your default value happens to be, especially in another assembly. That’s another consideration you need to make.

    Matt

  • http://blog.fujiy.net/ Felipe Fujiy

    A question about Named Parameters. It can generate a bug? Imagine that TextBoxInfo class in another DLL.

    When compiler create instances with default value. The value is static, even if I change default value in another DLL. Then i need to recompile DLL that calls TextBoxInfo.

    Sorry for bad english

  • James Brechtel

    Ahhh, you’re right. I was mistaken…I read that argument against optional parameters elsewhere. Thanks for the correction.

  • http://podwysocki.codebetter.com Matthew.Podwysocki

    @James

    I don’t remember that part of the interview about optional parameters. What I do remember is about virtual/override and what version problems it tries to solve. That’s what he was talking about when it comes to versioning. As for optional versus normal parameters, there are rules in place that I mentioned in the previous post here:
    http://codebetter.com/blogs/matthew.podwysocki/archive/2008/10/28/named-and-optional-arguments-in-c-4-0.aspx

    Matt

  • James

    I believe that Anders said (in a SE Radio podcast) that the reason for not having optional parameters in C# was for the sake of versioning. If the calling code stores the value for the default parameters when it calls a library then the library author can no longer safely change the default value in his library.

    Looking at the generated code, it seems like the default value is stored with the calling code.

    Are there any measures being taken to address the original verioning concerns?

  • http://podwysocki.codebetter.com Matthew.Podwysocki

    @Stu

    Well, for immutable types, I would say that you would use the constructor. I’m not a huge fan of the object initializers as they say by default you have mutability, when maybe it’s not required. So, I would stick with initializing as much as you can through the use of a constructor. If something needs mutation, then allow a setter on the property and do it that way.

    Matt

  • http://www.hackification.com Stu Smith

    Just wondered whether this will be the preferred way of passing optional parameters, compared to using the field setter syntax? We now have two “competing” ways:

    new TextBoxInfo( text: “Hello World”, size : 12.0f );

    and

    new TextBoxInfo{ Text = “Hello World”, Size = 12.0f };