如何在C# / .NET中使用LINQ表达式树调用lambda函数
如何在C# / .NET中使用LINQ表达式树调用lambda函数
我想使用表达式树来动态创建一个调用lambda的方法。以下代码对于第一次调用ComposeLambda函数运行良好,但第二次调用失败,并显示以下错误消息。
“为调用方法'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32)'提供的参数数量不正确。”
{ FuncinnerLambda = i => i + 1; var composedLambda = ComposeLambda(innerLambda); Console.WriteLine(composedLambda.DynamicInvoke(0)); var composedLambda2 = ComposeLambda(composedLambda); Console.WriteLine(composedLambda2.DynamicInvoke(0)); } private static Delegate ComposeLambda(Delegate innerLambda) { Func outerLambda = i => i + 2; var parameter = Expression.Parameter(typeof (int)); var callInner = Expression.Call(innerLambda.GetMethodInfo(), parameter); var callOuter = Expression.Call(outerLambda.GetMethodInfo(), callInner); var composedLambdaType = typeof (Func<,>).MakeGenericType(typeof (int), typeof (int)); var composedLambdaExpression = Expression.Lambda(composedLambdaType, callOuter, parameter); var composedLambda = composedLambdaExpression.Compile(); return composedLambda; }
如何获取并传递这个闭包对象?
在C# / .NET中使用LINQ表达式树调用lambda函数的问题是什么以及如何解决的?
问题是在调用lambda函数时,使用Expression.Call(innerLambda.GetMethodInfo(), ...)
是很麻烦的。相反,应该调用委托本身 - 你没有理由去操作委托的"方法" - 不仅会丢失目标(在实例方法中非常重要),还会违反隐私(例如,匿名方法是内部或私有的)。
解决方法是使用Expression.Invoke
(应该与委托一起使用),这样可以正常工作。
此外,如果在编译时知道正确的委托类型,不要使用Delegate
。在这种情况下,使用Func<int, int>
很简单,然后可以像这样调用:composedLambda2(0)
。
关于这个问题的解释和教程,可以参考MSDN的基本介绍:msdn.microsoft.com/en-us/library/bb397951.aspx。还可以查看blogs.msdn.com/b/charlie/archive/2008/01/31/…,其中包含一些解释和示例。
在这个例子中,表达式树的作用是什么?你可以直接构造委托,不需要使用表达式树吗?
是的,你可以使用outerLambda = i => innerLambda(i) + 2
直接实现相同的效果。实际上,如果你这样做(作为Expression<...>
),我相信它将生成相同的表达式树 - 或者非常相似的树。
Expression.Constant(innerLambda)
部分是如何工作的?ConstantExpression
如何与委托一起使用?
Expression.Constant
对于任何行为像常量值的东西都有效 - 也就是说,不依赖于表达式的其他部分,并且理想情况下是不可变的。它也适用于可变值,但请确保知道自己在做什么 - 大多数这些功能都是以函数式思维方式设计的,默认情况下是不可变的。当然,所有这些只有在编译表达式时才成立 - 如果将表达式用于其他用途(例如Entity Framework),则需要查找特定消费者的限制等信息。
一个常量值是什么?是对委托的引用还是委托的内部(返回值、副作用等)?
这没有硬性规定;尽管表达式被命名为Constant
,但它实际上意味着"这是按引用传递的"。引用被嵌入到表达式中(如果编译表达式树,则还包含在委托中)。当然,值类型会被复制(除非重用装箱的值)。现在,我倾向于对函数范围外的任何可变值持有怀疑态度。