替换 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<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() 或更复杂的格式? 嗯,收到的 lambda 可能有更多的参数,比如(f,g) => f.Method(g)
,我会有 f 和 g 的类型。关键实际上很普通,并不是一成不变的,任何合适的方式来表示((T)arg0,(V)arg1) => 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 语法 | 接口对象表达式 = 接口#函数类型对象 )