Cómo llamar a un lambda usando los árboles de expresiones LINQ en C # / .NET

.net c# expression-trees lambda linq

Pregunta

Quiero usar árboles de expresión para crear dinámicamente un método para llamar a lambda. El siguiente código funciona bien para la primera llamada a la función ComposeLambda, pero la segunda llamada falla con el siguiente mensaje de error.

Número incorrecto de argumentos proporcionados para la llamada al método '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ómo puedo obtener y transmitir este objeto de cierre?

Respuesta aceptada

No uses Expression.Call(innerLambda.GetMethodInfo(), ...) , eso es solo un problema. En su lugar, invoque al delegado: no tiene nada que ver con el "método" del delegado; no solo pierde el objetivo (también es muy importante en los métodos de instancia), sino que también viola la privacidad (los métodos anónimos son internos o privados, por ejemplo) ).

Y en este caso, no pasó el parámetro de cierre al método :) Esto debería ser bastante obvio en el mensaje de error: muestra la firma del método real (que incluye el cierre).

Si usa Expression.Invoke (como debería hacerlo con los delegados), funciona como se esperaba:

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

Además de esto, si conoce el tipo de delegado adecuado en tiempo de compilación, no use Delegate . En este caso, es bastante trivial utilizar Func<int, int> , que luego puede invocar como composedLambda2(0) , por ejemplo:

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow