如何在C#/ .NET中使用LINQ表达式树调用lambda

我想使用表达式树动态创建一个方法来调用lambda。 第一次调用ComposeLambda函数时,以下代码运行正常,但第二次调用失败,并显示以下错误消息。

为调用方法’Int32 lambda_method(System.Runtime.CompilerServices.Closure,Int32)提供的参数数量不正确

{ Func innerLambda = 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; } 

如何获取并传递此闭包对象?

不要使用Expression.Call(innerLambda.GetMethodInfo(), ...) ,这只是在寻找麻烦。 相反,调用委托 – 你没有业务搞乱委托的“方法” – 你不仅失去了目标(在实例方法中非常重要),而且你也违反了隐私(匿名方法是内部或私人的,例如)。

在这种情况下,您没有将closure参数传递给方法:)这应该从错误消息中显而易见 – 它向您显示实际方法的签名(包括闭包)。

如果您使用Expression.Invoke (就像您应该使用委托),它可以按预期工作:

 void Main() { Func innerLambda = 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.Invoke(Expression.Constant(innerLambda), parameter); var callOuter = Expression.Invoke(Expression.Constant(outerLambda), callInner); var composedLambdaType = typeof (Func<,>).MakeGenericType(typeof (int), typeof (int)); var composedLambdaExpression = Expression.Lambda(composedLambdaType, callOuter, parameter); var composedLambda = composedLambdaExpression.Compile(); return composedLambda; } 

除此之外,如果您在编译时知道正确的委托类型,请不要使用Delegate 。 在这种情况下,使用Func非常简单,然后可以将其作为composedLambda2(0)调用,例如:

 private static Func ComposeLambda(Func innerLambda) { Func outerLambda = i => i + 2; var parameter = Expression.Parameter(typeof (int)); var callInner = Expression.Invoke(Expression.Constant(innerLambda), parameter); var callOuter = Expression.Invoke(Expression.Constant(outerLambda), callInner); var composedLambdaExpression = Expression.Lambda>(callOuter, parameter); return composedLambdaExpression.Compile(); }