Das Kompilieren eines Lambda-Ausdrucks führt zu einem Delegaten mit Closure-Argument

c# expression-trees lambda

Frage

Wenn ich Expression.Lambda( ... ).Compile() verwende, um einen Delegaten aus einer Ausdrucksbaumstruktur zu erstellen, ist das Ergebnis ein Delegat, dessen erstes Argument Closure .

public static Func<T, T, T> CreateTest<T>()
{
    ParameterExpression a = Expression.Parameter( typeof( T ) );
    ParameterExpression b = Expression.Parameter( typeof( T ) );
    Expression addition = Expression.Add( a, b );

    return (Func<T, T, T>)Expression.Lambda( addition, a, b ).Compile();
}

...

// 'addition' equals
// Int32 lambda_method(
//     System.Runtime.CompilerServices.Closure,
//     Int32,
//     Int32 )
Func<int, int, int> addition = DelegateHelper.CreateTest<int>();
int result = addition( 5, 5 );

Ich kann den Delegierten einfach über gewöhnlichen Code anrufen, ohne ein Closure Objekt zu übergeben, aber woher kommt dieser Closure ?

Wie kann ich diesen Delegaten dynamisch aufrufen?

// The following does not work.
// Exception: MethodInfo must be a runtime MethodInfo object.    
MethodInfo additionMethod = addition.Method;
int result = (int)additionMethod.Invoke( null, new object[] { 5, 5 } );

Mit Hilfe von Ausdrucksbäumen sieht es so aus, als müsste ich das Closure Objekt übergeben.

PropertyInfo methodProperty
    = typeof( Delegate ).GetProperty( "Method", typeof( MethodInfo ) );
MemberExpression getDelegateMethod
    = Expression.Property( Expression.Constant( addition ), methodProperty );
Func<MethodInfo> getMethodInfo
    = (Func<MethodInfo>)Expression.Lambda( getDelegateMethod ).Compile();
// Incorrect number of arguments supplied for call to method
// 'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32, Int32)'
Expression call
    = Expression.Call(
        getMethodInfo(),
        Expression.Constant( 5 ), Expression.Constant( 5 ) );

Dies ist ein vereinfachtes Beispiel, das an sich keinen Sinn ergibt. Was ich eigentlich erreichen Func<Action<SomeObject>> ist, dass ich zB Func<Action<SomeObject>> mit Func<Action<object>> . Ich kann dies bereits für nicht verschachtelte Delegierte tun. Dies ist nützlich während der Reflexion, wie hier besprochen .

Wie soll ich dieses Closure Objekt korrekt initialisieren oder wie verhindere ich, dass es dort ist?

Akzeptierte Antwort

Der Closure Typ, den Sie sehen, ist ein Implementierungsdetail. Das MSDN ist ziemlich explizit darüber:

Diese API unterstützt die .NET Framework-Infrastruktur und ist nicht für die direkte Verwendung in Ihrem Code vorgesehen. Stellt den Laufzeitstatus einer dynamisch generierten Methode dar.

Ein Ausdrucksbaum kann einen Zustand haben.

Die Closure-Instanz enthält alle nicht-literalen Konstanten, die der Lambda-Ausdruck well (schließt). Es kann auch eine Kette von Delegaten für verschachtelte Lambdas in Ausdrucksbäumen enthalten.

Um dies zu erreichen, verwendet der Expression Tree Compiler einen niedlichen kleinen Trick. Es erzeugt im Speicher Code mit einer DynamicMethod , das ist per Definition statisch. Sie schaffen jedoch einen Delegierten, der über sein erstes Argument "geschlossen ist . Das bedeutet, dass die CLR das Zielfeld des Delegaten als erstes Argument der statischen Methode weitergibt, also müssen Sie nicht. Das Closure-Argument wird von Ihnen effektiv ausgeblendet.

Die Lösung für Ihr Problem ist einfach. Versuchen Sie nicht, die Methode aufzurufen, rufen Sie den Delegaten auf, indem Sie entweder Delegate.DynamicInvoke bei der Verwendung von Reflektion oder Expression.Invoke im Kontext einer Ausdrucksstruktur verwenden.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum