views:

248

answers:

2

I had a part of code that takes in lambda expressions at runtime, which I can then compile and invoke.

Something thing;

Expression<Action<Something>> expression = (c => c.DoWork());
Delegate del = expression.Compile();
del.DynamicInvoke(thing);

In order to save execution time, I stored those compiled delegates in a cache, a Dictionary<String, Delegate> which the key is the lambda expression string.

cache.Add("(Something)c => c.DoWork()", del);

For exact same calls, it worked fine. However I realized that I could receive equivalent lambdas, such as "d => d.DoWork()", which I should actually use the same delegate for, and I wasn't.

This got me wondering if there was a clean way (read "not using String.Replace", I already did that as a temporary fix) to replace the elements in a lambda expression, like maybe replacing them by arg0 so that both

(c => c.DoWork()) and (d => d.DoWork())

are transformed and compared as (arg0 => arg0.DoWork()) by using something fuctionnally similar to injecting a Expression.Parameter(Type, Name) in a lambda.

Is that possible ? (Answers can include C#4.0)

+1  A: 

There's more to a lambda expression than just the text. Lambdas are re-written by the compiler into something much more complicated. For example, they may close over variables (which could include other delegates). This means that two lambdas could look exactly the same, but perform completely different actions.

So you might have luck caching your compiled expression, but you need to give them a more meaningful name for the key than just the text of the expression. Otherwise no amount of argument substitution will help you.

Joel Coehoorn
+2  A: 

I used strings, since it was the easisest way for me. You can't manually change the name of the parameter expression (it has the "Name" property, but it is read-only), so you must construct a new expression from pieces. What I did is created a "nameless" parameter (actually, it gets an autogenerated name in this case, which is "Param_0") and then created a new expression almost the same as the old one, but using the new parameter.

public static void Main()
{
    String thing = "test";

    Expression<Action<String>> expression = c => c.ToUpper();
    Delegate del = expression.Compile();
    del.DynamicInvoke(thing);

    Dictionary<String, Delegate> cache = new Dictionary<String, Delegate>();
    cache.Add(GenerateKey(expression), del);

    Expression<Action<String>> expression1 = d => d.ToUpper();
    var test = cache.ContainsKey(GenerateKey(expression1));
    Console.WriteLine(test);

}

public static string GenerateKey(Expression<Action<String>> expr)
{
    ParameterExpression newParam = Expression.Parameter(expr.Parameters[0].Type);
    Expression newExprBody = Expression.Call(newParam, ((MethodCallExpression)expr.Body).Method);
    Expression<Action<String>> newExpr = Expression.Lambda<Action<String>>(newExprBody, newParam);
    return newExpr.ToString();
}
Alexandra Rusina
In the end I did fall back to something similar :) But I'm still looking for a more representative way to compare lambda-expressions
Dynami Le Savard