替换 lambda 表达式中的参数

Posted

技术标签:

【中文标题】替换 lambda 表达式中的参数【英文标题】:Replacing parameters in a lambda expression 【发布时间】:2011-01-07 10:44:58 【问题描述】:

我有一部分代码在运行时接受 lambda 表达式,然后我可以对其进行编译和调用。

Something thing;

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

为了节省执行时间,我将那些编译好的委托存储在一个缓存中,一个Dictionary&lt;String, Delegate&gt;,其键是 lambda 表达式字符串。

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

对于完全相同的调用,它运行良好。但是我意识到我可以接收等效的 lambda,例如“d => d.DoWork()”,我实际上应该使用相同的委托,而我没有。

这让我想知道是否有一种干净的方法(阅读“不使用 String.Replace”,我已经将其作为临时修复)来替换 lambda 表达式中的元素,比如用arg0 替换它们这两个

(c =&gt; c.DoWork())(d =&gt; d.DoWork())

通过使用类似于在 lambda 中注入 Expression.Parameter(Type, Name) 的功能被转换和比较为 (arg0 =&gt; arg0.DoWork())

这可能吗? (答案可以包括 C#4.0)

【问题讨论】:

密钥是否始终采用以下格式:(type)arg => arg.Method() 或更复杂的格式? 嗯,收到的 lambda 可能有更多的参数,比如(f,g) =&gt; f.Method(g),我会有 f 和 g 的类型。关键实际上很普通,并不是一成不变的,任何合适的方式来表示((T)arg0,(V)arg1) =&gt; arg0.Method(arg1) 旁边的 2 个 lambda 之间的等价性都可以作为关键。 【参考方案1】:

我使用字符串,因为这对我来说是最简单的方法。您不能手动更改参数表达式的名称(它具有“名称”属性,但它是只读的),因此您必须从片段中构造一个新表达式。我所做的是创建了一个“无名”参数(实际上,在这种情况下,它获得了一个自动生成的名称,即“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 表达式 @Alexandra 我正在尝试使用相同的主体表达式但具有不同类型参数的现有 MethodCallExpression 构造一个新的 MethodCallExpression。可以使用类似于您的 GenerateKey 方法的方法来完成此操作吗?见:***.com/questions/14363387/…【参考方案2】:

lambda 表达式不仅仅是文本。编译器将 Lambda 重写为更复杂的东西。例如,他们可能会关闭变量(可能包括其他委托)。这意味着两个 lambda 可能看起来完全相同,但执行完全不同的操作。

因此,您可能会幸运地缓存已编译的表达式,但您需要为键指定一个更有意义的名称,而不仅仅是表达式的文本。否则,再多的参数替换也无济于事。

【讨论】:

以上是关于替换 lambda 表达式中的参数的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin函数 ⑦ ( 内联函数 | Lambda 表达式弊端 | “ 内联 “ 机制避免内存开销 - 将使用 Lambda 表达式作为参数的函数定义为内联函数 | 内联函数本质 - 宏替换 )

挑苹果中的行为参数化思想

函数式编程———内联函数

KotlinKotlin 中使用 Lambda 表达式替代对象表达式原理分析 ( 尾随 Lambda - Trailing Lambda 语法 | 接口对象表达式 = 接口#函数类型对象 )

Python中的匿名函数——lambda函数

语句 lambda 可以替换为表达式 lambda