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

本文关键字:LINQ 表达式 lambda 调用 NET | 更新日期: 2023-09-27 17:56:52

我想使用表达式树来动态创建一个调用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;
}

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

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

不要使用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();
}