如何在 C#/.NET 中使用 LINQ 表达式树调用 lambda
How to call a lambda using LINQ expression trees in C# / .NET
我想使用表达式树动态创建调用 lambda 的方法。以下代码在第一次调用 ComposeLambda 函数时运行良好,但第二次调用失败并显示以下错误消息。
Incorrect number of arguments supplied for call to method 'Int32
lambda_method(System.Runtime.CompilerServices.Closure, Int32)'
{
Func<int, int> 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<int, int> 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(), ...)
,那是自找麻烦。相反,调用委托 - 你没有业务与委托的 "method" 混为一谈 - 你不仅失去了目标(在实例方法中非常重要),而且还侵犯了隐私(匿名方法是内部的或私有的,例如)。
在这种情况下,您没有将闭包参数传递给方法 :) 这在错误消息中应该很明显 - 它向您显示了实际方法(包括闭包)的签名。
如果您使用 Expression.Invoke
(您应该使用委托),它会按预期工作:
void Main()
{
Func<int, int> 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<int, int> 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<int, int>
非常简单,然后您可以将其调用为 composedLambda2(0)
,例如:
private static Func<int, int> ComposeLambda(Func<int, int> innerLambda)
{
Func<int, int> 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<Func<int, int>>(callOuter, parameter);
return composedLambdaExpression.Compile();
}
我想使用表达式树动态创建调用 lambda 的方法。以下代码在第一次调用 ComposeLambda 函数时运行良好,但第二次调用失败并显示以下错误消息。
Incorrect number of arguments supplied for call to method 'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32)'
{
Func<int, int> 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<int, int> 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(), ...)
,那是自找麻烦。相反,调用委托 - 你没有业务与委托的 "method" 混为一谈 - 你不仅失去了目标(在实例方法中非常重要),而且还侵犯了隐私(匿名方法是内部的或私有的,例如)。
在这种情况下,您没有将闭包参数传递给方法 :) 这在错误消息中应该很明显 - 它向您显示了实际方法(包括闭包)的签名。
如果您使用 Expression.Invoke
(您应该使用委托),它会按预期工作:
void Main()
{
Func<int, int> 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<int, int> 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<int, int>
非常简单,然后您可以将其调用为 composedLambda2(0)
,例如:
private static Func<int, int> ComposeLambda(Func<int, int> innerLambda)
{
Func<int, int> 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<Func<int, int>>(callOuter, parameter);
return composedLambdaExpression.Compile();
}