So first off... I'm surely not the first person to see the resemblance between a lambda expression and a hash table declaration in Ruby... so I had a go at populating a dictionary using lambda's... so given this:
[Test]
public void Evaluate()
{
Dictionary<string, string> items = Hash(Name => "alex", Age => "10", Height => "20");
Assert.AreEqual("alex", items["Name"]);
Assert.AreEqual("10", items["Age"]);
Assert.AreEqual("20", items["Height"]);
}
I got my desired result pretty quickly...
public Dictionary<string, T> Hash<T>(params Expression<Func<string, T>>[] args)
where T: class
{
Dictionary<string, T> items = new Dictionary<string, T>();
foreach (Expression<Func<string, T>> expression in args)
{
ConstantExpression itemValueExpression = expression.Body as ConstantExpression;
if (itemValueExpression == null)
throw new InvalidCastException("The body of the expression must be of type ConstantExpression");
T item = itemValueExpression.Value as T;
items.Add(expression.Parameters[0].Name, item);
}
return items;
}
But, then I tried this...
[Test]
public void EvaluateForObjects()
{
Dictionary<string, object> items = Hash<object>(Name => "alex", TargetType => typeof(Uri), Id => 10);
Assert.AreEqual(10, items["Id"]);
}
Which fails, the last key/value pair (Id => 10) isn't represented as a ConstantExpression, so to support it (and other eventualities) I modified my code a smidge...
public Dictionary<string, T> Hash<T>(params Expression<Func<string, T>>[] args)
where T: class
{
Dictionary<string, T> items = new Dictionary<string, T>();
foreach (Expression<Func<string, T>> expression in args)
{
ConstantExpression constantExpression = expression.Body as ConstantExpression;
T item = null;
if (constantExpression != null)
{
item = constantExpression.Value as T;
}
else
{
item = Expression.Lambda<Func<T>>(expression.Body).Compile()();
}
items.Add(expression.Parameters[0].Name, item);
}
return items;
}
Now... let's compare performance to say, adding the values using the Add method... so... for a million consecutive executions (in milliseconds) a quick test yielded these results...
| Execution # |
Using 'Hash' |
Using Dictionary.Add() |
| 1 |
9718 |
1268 |
| 2 |
9588 |
1310 |
| 3 |
9636 |
1494 |
Which I suspect tells me nothing more then the fact that the "Hash" style initialization is a lot slower, but not slow enough to completely discourage me.
Edit: There seems to be a bit of interest in this lately, so don't forget to take a look at the other related posts if you found this interesting.