Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing parameters in a lambda expression

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)

like image 291
Dynami Le Savard Avatar asked Jan 20 '10 19:01

Dynami Le Savard


People also ask

How to replace lambda expression?

If you are using a lambda expression as an anonymous function but not doing anything with the argument passed, you can replace lambda expression with method reference. In the first two cases, the method reference is equivalent to lambda expression that supplies the parameters of the method e.g. System.

Is it makes sense to replace lambda expression with method references?

To summarize: If the purpose of the lambda expression is solely to pass a parameter to an instance method, then you may replace it with a method reference on the instance. If the pass-through is to a static method, then you may replace it with a method reference on the class.

Can you use ref and out parameters in lambda expression if declared outside?

Lambda expressions can make use of variables declared in a containing scope, i.e. outside of the expression itself. They cannot, however, use variables that are defined as ref or out parameters in an outer scope.

Can we write parameter less lambda expression?

No, there isn't. Lambda expressions are optimised (in terms of syntax) for the single parameter case. I know that the C# team feels your pain, and have tried to find an alternative.


2 Answers

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();
}
like image 172
Alexandra Rusina Avatar answered Sep 28 '22 02:09

Alexandra Rusina


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.

like image 41
Joel Coehoorn Avatar answered Sep 28 '22 02:09

Joel Coehoorn