A Better Dictionary Initializer

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


Print

Comments on this entry:

# re: A Better Dictionary Initializer

Left by Ayende Rahien at 12/29/2007 11:11 PM
Gravatar

Just to point out, Compile() is likely to be an expensive call.
You can the parameter value from the expression tree for free.

# re: A Better Dictionary Initializer

Left by Alex Henderson at 12/30/2007 2:07 PM
Gravatar

Don't use an Expression at all, it's much slower then just using Func<object, object> - see this post on my blog from a while back, which covered the very same thing:

http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx

I ended up using this approach when implementing a "pseduo-DSL" on my blog a little later:

http://blog.bittercoder.com/PermaLink,guid,f962b2b9-38ba-4dec-8305-50501e0e21f0.aspx

The source code can be found here if your curious as well:

http://trac.devdefined.com/public/trac/tools.devdefined.com/browser/trunk/src/DevDefined.Common

# The Weekly Source Code 12 - Back in Black Edition

Left by Scott Hanselman's Computer Zen at 1/8/2008 12:43 PM

# The Weekly Source Code 12 - Back in Black Edition

Left by Scott Hanselman's Computer Zen at 1/8/2008 12:44 PM

# The Weekly Source Code 12 - Back in Black Edition

Left by Scott Hanselman's Computer Zen at 1/8/2008 12:45 PM

# The Weekly Source Code 12 - Back in Black Edition

Left by ASPInsiders at 1/8/2008 1:10 PM

It's been a while since the last Weekly Source Code , but I have the holidays and the preponderance of

Your comment:



 (will not be displayed)


 
 
 
Please add 7 and 2 and type the answer here:
 

Live Comment Preview:

 
«July»
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789