Jacob Carpenter’s Weblog

November 19, 2007

Named parameters { Part = 2 }

Filed under: csharp — Jacob @ 7:19 am

Note: the following is presented merely as an interesting diversion. It’s not a technique I’m legitimately proposing to the C# community, though I certainly welcome any feedback you may have.

Continuing our discussion from last time, we’re going to look at what we can do with object initialization syntax to enable immutable type initialization. But first we’re going to need a contrived example immutable type:

public class CoffeeOrder 
{ 
    public string DrinkName 
    { 
        get { return m_drinkName; } 
    } 
    public bool AddSugar 
    { 
        get { return m_addSugar; } 
    } 
    public bool IncludeRoom 
    { 
        get { return m_includeRoom; } 
    } 
    public bool IsDecaf 
    { 
        get { return m_isDecaf; } 
    } 
    public int SizeOunces 
    { 
        get { return m_sizeOunces; } 
    } 

    readonly string m_drinkName; 
    readonly bool m_addSugar; 
    readonly bool m_includeRoom; 
    readonly bool m_isDecaf; 
    readonly int m_sizeOunces; 
}

This is obviously not a post on type design.

So we want to be able create instances of these using initialization-like syntax. We also don’t want to define a number of constructors to accommodate all of the combinations of optional parameters.

The simplest way we can achieve these goals would be to create a CoffeeOrder constructor that took an object. Then, using reflection, we could examine the properties of any passed-in value to see what fields we should set.

public CoffeeOrder() { } 

public CoffeeOrder(object initializer) 
{ 
    /* reflect over "initializer" and set fields */ 
}

We could then create CoffeOrders like so:

CoffeeOrder order = new CoffeeOrder(new 
{ 
    DrinkName = "Drip", 
    SizeOunces = 16, 
});

Unfortunately, that wouldn’t be very reusable. Every time we defined a new immutable type, we’d need to rewrite the reflection code. We also aren’t being very clear to the caller what the passed in object is supposed to be.

Let’s create an abstraction for the initializer parameter:

public class NamedArgs 
{ 
    public static NamedArgs Create(object initializer) 
    { 
        return new NamedArgs { m_data = initializer, m_datatype = initializer.GetType() }; 
    } 

    object m_data; 
    Type m_datatype; 
}

Our CoffeeOrder constructor signature becomes:

public CoffeeOrder(NamedArgs args)

And the calling code is:

CoffeeOrder order = new CoffeeOrder(NamedArgs.Create(new 
{ 
    DrinkName = "Drip", 
    SizeOunces = 16, 
}));

We’ve improved the CoffeeOrder constructor signature a bit. It’s certainly more descriptive of what is expected. The code to call the constructor is incrementally less beautiful now, though.

But let’s move on and see about actually getting data out of this NamedArgs object.

We’re going to use reflection APIs (System.Reflection) to do so, though we’re only going to scratch the surface of what’s possible. Right now, we’re just going to look up property values by string names. We’ll look at a more interesting way to refer to the properties before we’re done.

public class NamedArgs 
{ 
    // ... 

    public void SetFromArg<TValue>(string argumentName, ref TValue result) 
    { 
        // get the PropertyInfo for the requested argument (if available) 
        PropertyInfo infoProp = m_datatype.GetProperty(argumentName); 
        if (infoProp != null) 
        { 
            // set the result 
            result = (TValue) infoProp.GetValue(m_data, null); 
        } 
    } 
}

Rather than returning default(TValue) when the argument isn’t specified, or creating a TryGetValue sort of API, we’re only going to set the referenced value if the NamedArgs instance contains an argument matching the name. That way, the class that calls this method can set up default values before calling our method and not worry about explicitly handling any unspecified, optional arguments. So let’s update the anemic CoffeeOrder constructor:

public CoffeeOrder(NamedArgs args) 
{ 
    args.SetFromArg("DrinkName", ref m_drinkName); 
    args.SetFromArg("AddSugar", ref m_addSugar); 
    args.SetFromArg("IncludeRoom", ref m_includeRoom); 
    args.SetFromArg("IsDecaf", ref m_isDecaf); 
    args.SetFromArg("SizerOunces", ref m_sizeOunces); 
}

That’s surprisingly unoffensive. I mean, sure, there are a lot of magic strings, but it’s not the worst thing I’ve ever seen.

On the other hand, it does kind of suck. We can improve it, though, using the power of expression trees.

Property accesses are a type of expression (MemberExpression to be specific). We can pass in a lambda that performs property access on an argument and we can treat that expression as data to figure out the name of the member that’s being accessed.

So in the case of our CoffeeOrder constructor calling SetFromArg, we want an expression of type Expression<Func<CoffeeOrder, T>> where T is the target property’s value type. But CoffeeOrder will be the type parameter for all of the expressions, and we don’t want to lose our type inference on the ref parameter (if we specify one type argument to SetFromArg, we have to specify them all). So let’s create a new derivation of NamedArgs to hold that fixed type argument:

public class NamedArgs 
{ 
    /* ... */ 

    public static NamedArgs<T> For<T>(object initializer) 
    { 
        return new NamedArgs<T> { m_data = initializer, m_datatype = initializer.GetType() }; 
    } 
}

public class NamedArgs<T> : NamedArgs 
{ 
    public void SetFromArg<TValue>(Expression<Func<T, TValue>> propertyAccessor, ref TValue result) 
    { 
        // examine the expression tree for member access 
        MemberExpression accessor = propertyAccessor.Body as MemberExpression; 
        if (accessor == null) 
            throw new ArgumentException("Invalid expression type. Expecting member access.", "propertyAccessor"); 

        // set the value from the specified argument name 
        SetFromArg(accessor.Member.Name, ref result); 
    } 
}

We added the For method to the non-generic NamedArgs class just for aesthetics. NamedArgs.For<CoffeeOrder>(/*...*/) looks better than NamedArgs<CoffeeOrder>.For(/*...*/).

Now our CoffeeOrder constructor looks like:

public CoffeeOrder(NamedArgs<CoffeeOrder> args) 
{ 
    args.SetFromArg(c => c.DrinkName, ref m_drinkName); 
    args.SetFromArg(c => c.AddSugar, ref m_addSugar); 
    args.SetFromArg(c => c.IncludeRoom, ref m_includeRoom); 
    args.SetFromArg(c => c.IsDecaf, ref m_isDecaf); 
    args.SetFromArg(c => c.SizeOunces, ref m_sizeOunces); 
}

Which is nice because now we’re even more explicit about what the CoffeeOrder constructor is expecting. And, when we’re writing the calls to SetFromArg, we get intellisense for the available properties.

Our calling code becomes:

CoffeeOrder order = new CoffeeOrder(NamedArgs.For<CoffeeOrder>(new 
{ 
    DrinkName = "Drip", 
    SizeOunces = 16, 
}));

Which is certainly on the verbose side, but not abhorrent.

Closing

We’ve created a type that encapsulates the notion of named parameters with the intent of using it to construct immutable types. We’ve tailored some behaviors specifically to that intended usage, but it’s not closed from use by other methods.

Since anonymous types are immutable, they are thread-safe. The NamedArgs class with its fields that reference immutable type instances is thread-safe as well (though callers will obviously have to protect any values passed as ref parameters).

There are certainly features we could add. One commonly requested feature for immutable types is the ability to initialize one instance from another instance with only a few overridden values. Something like:

public static NamedArgs<T> From<T>(T baseValue, object overrides)

There are also certainly drawbacks to our technique. Some would argue the use of reflection alone is a drawback.

More discerning folks will point out that it’s terribly easy to give incorrectly named arguments. It’s nice that we got intellisense support while implementing the constructor by using expression trees, but we don’t have any such niceties when we’re creating a NamedArgs instance used for calling that constructor.

A more robust solution could certainly validate the property names for generic NamedArgs instances, but that would involve extra reflection.

I hope this post has been enjoyable and inspires other creative solutions. I’d love to hear any feedback you have on this.

Full example source follows:

using System; 
using System.Linq.Expressions; 
using System.Reflection; 

class Program 
{ 
    static void Main(string[] args) 
    { 
        var order1 = new CoffeeOrder(NamedArgs.For<CoffeeOrder>(new 
        { 
            DrinkName = "Drip", 
            SizeOunces = 16, 
        })); 

        var order2 = new CoffeeOrder(NamedArgs.For<CoffeeOrder>(new 
        { 
            DrinkName = "Drip", 
            SizeOunces = 12, 
            IsDecaf = true, 
            AddSugar = true, 
        })); 

        Console.WriteLine(order1); 
        Console.WriteLine(order2); 

        if (System.Diagnostics.Debugger.IsAttached) 
        { 
            Console.Write("Press any key to continue . . . "); 
            Console.ReadKey(true); 
        } 
    } 
} 

public class NamedArgs 
{ 
    public static NamedArgs Create(object initializer) 
    { 
        return new NamedArgs { m_data = initializer, m_datatype = initializer.GetType() }; 
    } 

    public static NamedArgs<T> For<T>(object initializer) 
    { 
        return new NamedArgs<T> { m_data = initializer, m_datatype = initializer.GetType() }; 
    } 

    public void SetFromArg<TValue>(string argumentName, ref TValue result) 
    { 
        // get the PropertyInfo for the requested argument (if available) 
        PropertyInfo infoProp = m_datatype.GetProperty(argumentName); 
        if (infoProp != null) 
        { 
            // set the result 
            result = (TValue) infoProp.GetValue(m_data, null); 
        } 
    } 

    object m_data; 
    Type m_datatype; 
} 

public class NamedArgs<T> : NamedArgs 
{ 
    public void SetFromArg<TValue>(Expression<Func<T, TValue>> propertyAccessor, ref TValue result) 
    { 
        // examine the expression tree for member access 
        MemberExpression accessor = propertyAccessor.Body as MemberExpression; 
        if (accessor == null) 
            throw new ArgumentException("Invalid expression type. Expecting member access.", 
                "propertyAccessor"); 

        // set the value from the specified argument name 
        SetFromArg(accessor.Member.Name, ref result); 
    } 
} 

public class CoffeeOrder 
{ 
    public CoffeeOrder() { } 

    public CoffeeOrder(NamedArgs<CoffeeOrder> args) 
    { 
        args.SetFromArg(c => c.DrinkName, ref m_drinkName); 
        args.SetFromArg(c => c.AddSugar, ref m_addSugar); 
        args.SetFromArg(c => c.IncludeRoom, ref m_includeRoom); 
        args.SetFromArg(c => c.IsDecaf, ref m_isDecaf); 
        args.SetFromArg(c => c.SizeOunces, ref m_sizeOunces); 
    } 

    public string DrinkName 
    { 
        get { return m_drinkName; } 
    } 

    public bool AddSugar 
    { 
        get { return m_addSugar; } 
    } 

    public bool IncludeRoom 
    { 
        get { return m_includeRoom; } 
    } 

    public bool IsDecaf 
    { 
        get { return m_isDecaf; } 
    } 

    public int SizeOunces 
    { 
        get { return m_sizeOunces; } 
    } 

    public override string ToString() 
    { 
        return String.Format("Coffee Order: {0} oz. {1}\n   IsDecaf     = {2}\n" + 
            "   IncludeRoom = {3}\n   AddSugar    = {4}", SizeOunces, DrinkName, 
            IsDecaf, IncludeRoom, AddSugar); 
    } 

    readonly string m_drinkName; 
    readonly bool m_addSugar; 
    readonly bool m_includeRoom; 
    readonly bool m_isDecaf; 
    readonly int m_sizeOunces; 
}
About these ads

2 Comments »

  1. If all the parameters to the constructor are always passed, you could perhaps use this technique to access them without reflection:
    http://tomasp.net/articles/cannot-return-anonymous-type-from-method.aspx

    Comment by Bradley — November 21, 2007 @ 8:35 am

  2. [...] Filed under: Ruby, csharp — jacobcarpenter @ 3:16 pm In addition to the interesting diversions we’ve taken, I do want to continue presenting potentially useful code samples too. So [...]

    Pingback by Another set of extension methods « Jacob Carpenter’s Weblog — November 24, 2007 @ 3:21 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: