How to call a lambda using LINQ expression trees in C# / .NET

8k views Asked by At

I want to use expression trees to dynamically create a method to call a lambda. The following code runs fine for the first call to the ComposeLambda function, but the second call fails with the following error message.

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;
}

How can I get and pass on this closure object?

1

There are 1 answers

11
Luaan On BEST ANSWER

Don't use Expression.Call(innerLambda.GetMethodInfo(), ...), that's just asking for trouble. Invoke the delegate instead - you have no business messing around with the delegate's "method" - not only do you lose the target (quite important in instance methods), but you're also violating privacy (anonymous methods are internal or private, for example).

And in this case, you didn't pass the closure parameter to the method :) This should be rather obvious from the error message - it shows you the signature of the actual method (which includes the closure).

If you use Expression.Invoke (as you should with delegates), it works as expected:

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;
}

In addition to this, if you know the proper delegate type at compile-time, don't use Delegate. In this case, it's pretty trivial to use Func<int, int>, which you can then invoke as composedLambda2(0), for example:

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();
}