用lambda表达式替换参数

8

我有一部分代码,可以在运行时接收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);

对于完全相同的调用,它能够正常工作。但是我意识到我可以接收等效的lambda表达式,例如“d => d.DoWork()”,我实际上应该使用相同的委托,但我没有这样做。
这让我想知道是否有一种干净的方法(即“不使用String.Replace”,我已经暂时解决了这个问题),可以替换lambda表达式中的元素,比如用“arg0”替换它们,以便两者
(c => c.DoWork()) 和 (d => d.DoWork())
通过使用类似于在lambda中注入Expression.Parameter(Type, Name)的功能来转换和比较为(arg0 => arg0.DoWork())。
这可能吗?(答案可以包括C#4.0)

关键字的格式是否总是(type)arg => arg.Method(),还是预计会有更复杂的格式? - Elisha
好的,接收到的lambda表达式可能有更多的参数,例如(f,g) => f.Method(g),我将拥有f和g的类型。关键是相当平凡的,而且并不是一成不变的,除了((T)arg0,(V)arg1) => arg0.Method(arg1)之外,任何适合表示两个lambda表达式等价的方法都可以作为关键。 - Dynami Le Savard
2个回答

3

我使用了字符串,因为这对我来说是最简单的方式。你无法手动更改参数表达式的名称(它有一个“Name”属性,但只读),所以必须从各个部分构建新的表达式。我的做法是创建了一个“匿名”的参数(实际上,在这种情况下,它得到了一个自动生成的名称,即“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();
}

最终我还是回到了类似的东西 :) 但我仍在寻找一种更具代表性的比较lambda表达式的方式。 - Dynami Le Savard
@Alexandra 我正在尝试使用现有的MethodCallExpression构建一个新的MethodCallExpression,使用相同的body表达式但不同的类型参数。是否可以使用类似于您的GenerateKey方法的东西来实现这一点?请参见:http://stackoverflow.com/questions/14363387/how-can-i-repurpose-a-generic-c-sharp-expression-allowing-the-input-type-argumen#comment19974655_14363387 - Vinney Kelly

3

lambda表达式不仅是一段文本,它们还会被编译器转换成更复杂的形式。例如,它们可能会关闭变量(包括其他委托)。这意味着两个lambda可能看起来完全相同,但执行完全不同的操作。

因此,如果您想缓存已编译的表达式,您需要为键提供更有意义的名称,而不仅仅是表达式的文本。否则,无论如何替换参数都没有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接