我想使用表達式樹動態創建一個方法來調用lambda。第一次調用ComposeLambda函數時,以下代碼運行正常,但第二次調用失敗,並顯示以下錯誤消息。
為調用方法'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(), ...)
,這只是在尋找麻煩。相反,調用委託 - 你沒有業務搞亂委託的“方法” - 你不僅失去了目標(在實例方法中非常重要),而且你也違反了隱私(匿名方法是內部或私人的,例如)。
在這種情況下,您沒有將closure參數傳遞給方法:)這應該從錯誤消息中顯而易見 - 它向您顯示實際方法的簽名(包括閉包)。
如果您使用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();
}