Monday, June 11, 2007
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.

 |  | 
Monday, June 11, 2007 9:09:32 PM (New Zealand Standard Time, UTC+12:00)
Since there is no way to do it without Compile(), there is a much more performant method (at least in my tests):
public Dictionary<string, T> Hash<T>(params Func<string, T>[] args)
where T: class
{
var items = new Dictionary<string, T>();
foreach (var func in args)
{
var item = func(null);
items.Add(func.Method.GetParameters()[0].Name, item);
}
return items;
}

For 10000 consecutive calls:
WithAdd: 10.0144ms
WithLambdas: 9713.968ms
WithLambdasConstantsOnly: 240.3456ms
WithDelegates (my version): 30.0432ms
Monday, June 11, 2007 9:39:53 PM (New Zealand Standard Time, UTC+12:00)
Very clever, I never thought to use the parameter name of an anonymous delegate - Love it!
Comments are closed.
Search
FeedCount

Tags...
Who am I?
Alex Henderson
Alex Henderson
Auckland, New Zealand
Managing Director at Dev|Defined Limited

"Self Confessed Coding Junky for 15 years"
View Alex Henderson's profile on LinkedIn
 
Mobile: +64-21-402-969
Email: bittercoder 'at' gmail 'dot' com
MSN: bittercoder_nz@hotmail
Skype: alex.devdefined
Navigation