Wie Sie ein Lambda mit LINQ-Ausdrucksbäumen in C # / .NET aufrufen

.net c# expression-trees lambda linq

Frage

Ich möchte Ausdrucksbäume verwenden, um dynamisch eine Methode zum Aufrufen eines Lambda zu erstellen. Der folgende Code funktioniert ordnungsgemäß für den ersten Aufruf der ComposeLambda-Funktion, aber der zweite Aufruf schlägt mit der folgenden Fehlermeldung fehl.

Falsche Anzahl der übergebenen Argumente für den Aufruf der Methode '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;
}

Wie kann ich dieses Verschlussobjekt erhalten und weitergeben?

Akzeptierte Antwort

Verwenden Sie nicht Expression.Call(innerLambda.GetMethodInfo(), ...) , die nur nach Ärger fragen. Rufen Sie stattdessen den Delegaten auf - Sie haben nichts mit der "Methode" des Delegaten zu tun - Sie verlieren nicht nur das Ziel (ziemlich wichtig in Instanzmethoden), sondern verletzen auch die Privatsphäre (anonyme Methoden sind zum Beispiel intern oder privat) ).

Und in diesem Fall haben Sie den Closure-Parameter nicht an die Methode übergeben :) Dies sollte aus der Fehlermeldung ziemlich offensichtlich sein - es zeigt Ihnen die Signatur der tatsächlichen Methode (die die Schließung beinhaltet).

Wenn Sie Expression.Invoke (wie Sie es bei Delegaten tun sollten), funktioniert es wie erwartet:

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

Wenn Sie den richtigen Delegattyp zur Kompilierzeit kennen, verwenden Sie Delegate . In diesem Fall ist es ziemlich trivial, Func<int, int> , das Sie dann zum Beispiel als Func<int, int> composedLambda2(0) aufrufen können:

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


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow