Collection initializers are one of the new language features for C# 3.0. They are meant to give your wrists a break by allowing you to instantiate and populate a collection in one fell swoop. ScottGu has a nice post that shows off the new syntax for Lists and Wriju shows you how to initialize a Dictionary. The new syntax is nice but I think we can make a few improvements. Dictionaries are heavily used in MonoRail/ASP.Net MVC for passing name/value pairs to functions. One particular usage is to pass in additional attributes for methods that render html (class="text", size=15, title="Click Here", alt="[The Big Picture]", etc). Let's take the following method:
public string TextBox(string name, object value, IDictionary attributes)
{
// Generate html for an input[type=text] field
}
If we wanted to use this function using the Dictionary initializer syntax, we would end up with something like:
TextBox("name", string.Empty, new Hashtable() { { "class", "text" }, { "size", 15 } });
That is awfully verbose, especially for those who come from the land of dynamic languages like Ruby and Boo. Let's see if we can do a little better using another new feature for C# 3.0: Anonymous Types. Our code above turns into:
TextBox("name", string.Empty, new{ @class = "text", size = 15 });
This is quite an improvement. We've eliminated nearly twenty characters and our wrists feel much better. For this code to work we must add a TextBox overload:
public string TextBox(string name, object value, object attributes)
{
return TextBox( name, value, Hash.Init( attributes ) );
}
And a static Hash class with Init method:
public static IDictionary Init(object anonymous)
{
IDictionary values = new Hashtable(StringComparer.OrdinalIgnoreCase);
if (anonymous == null) return values;
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(anonymous))
{
values.Add(property.Name, property.GetValue(anonymous));
}
return values;
}
But this is nothing new you cry! Rob Conery bespoke something similar earlier this month. Let's see if we can do a little better using another new feature for C# 3.0: Lamda Expressions. Our code above turns into:
TextBox("name", string.Empty, @class => "text", size => 15);
This is a slight improvement, only a savings of a three characters, but it has some sex appeal. Your inner Rubyist is nodding with approval. For this code to work we must add another TextBox overload:
public string TextBox(string name, object value, params Expression<Func<object, object>>[] expressions)
{
return TextBox(name, value, Hash.Init(expressions));
}
And an overload to our Hash.Init:
public static IDictionary Init(params Expression<Func<object, object>>[] expressions)
{
IDictionary values = new Hashtable(expressions.Length, StringComparer.OrdinalIgnoreCase);
foreach (Expression<Func<object, object>> expression in expressions)
{
string key = expression.Parameters[0].Name;
Func<object, object> function = expression.Compile();
object value = function(null);
values.Add(key, value);
}
return values;
}
Pretty sweet and only a little bit of voodoo. Proponents of lambda expressions say it makes your code more readable. Whether or not that's the case, I think in this example the code is more expressive and saves you some typing. Now, go refactor all of your IDictionary parameters :)
posted @ Saturday, December 29, 2007 7:38 AM