我有一部分代码在运行时接受lambda表达式,然后我可以编译和调用它。
Something thing;
Expression<Action<Something>> expression = (c => c.DoWork());
Delegate del = expression.Compile();
del.DynamicInvoke(thing);
为了节省执行时间,我将这些已编译的委托存储在缓存中,Dictionary<String, Delegate>
,其中键是lambda表达式字符串。
cache.Add("(Something)c => c.DoWork()", del);
对于完全相同的调用,它工作正常。但是我意识到我可以收到相同的lambdas,例如“d =&gt; d.DoWork()”,我实际上应该使用相同的委托,而我不是。
这让我想知道是否有一种干净的方式(读取“不使用String.Replace”,我已经将其作为临时修复)来替换lambda表达式中的元素,例如可能用{{1}替换它们这两个
arg0
和(c => c.DoWork())
通过使用与在lambda中注入Expression.Parameter(Type,Name)类似的东西进行转换和比较为(d => d.DoWork())
。
这可能吗? (答案可以包括C#4.0)
答案 0 :(得分:3)
lambda表达式不仅仅是文本。 Lambdas被编译器重写为更复杂的东西。例如,他们可能会关闭变量(可能包括其他代表)。这意味着两个lambda看起来完全相同,但执行完全不同的操作。
所以你可能有运气缓存你编译的表达式,但你需要为它们提供一个更多有意义的名称,而不仅仅是表达式的文本。否则,任何参数替换都不会对你有帮助。
答案 1 :(得分:3)
我使用字符串,因为这对我来说是最简单的方法。您无法手动更改参数表达式的名称(它具有“名称”属性,但它是只读的),因此您必须从多个部分构造一个新表达式。我所做的是创建一个“无名”参数(实际上,它在这种情况下获得一个自动生成的名称,即“Param_0”),然后创建一个与旧表达式几乎相同的新表达式,但使用新参数。
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();
}