Erstellen einer Ausdrucksstruktur, die eine Methode aufruft

c# delegates expression-trees lambda reflection

Frage

Ist es möglich, einen Ausdrucksbaum zu erstellen, der direkt eine Methode aufruft? Betrachten Sie beispielsweise die folgende Methode:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Ich möchte einen Ausdrucksbaum erstellen, der MyFunc mit den Parametern a = 1 und b = 2 aufruft. Eine Möglichkeit, dies zu erreichen, ist die Reflexion:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Dies ist jedoch nachteilig, da die Reflektion langsam ist und die Fehler bei der Kompilierung zu Laufzeitfehlern werden.

Ich könnte stattdessen den folgenden Ansatz verwenden:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Aber das ist immer noch nicht das, was ich will, weil es die Methode in einen Lambda-Ausdruck einfügt, anstatt es direkt aufzurufen.

Eine gute Lösung könnte folgendermaßen aussehen:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Leider wird das nicht kompiliert, da del ein Delegat und kein Ausdruck ist. Gibt es eine Möglichkeit, einen Ausdruck von einem Delegaten zu erstellen? (Beachten Sie, dass ich das Ziel des Delegaten zur Kompilierungszeit kenne, daher brauche ich nicht die hier beschriebene Flexibilität: Ausdrucksbaumstrukturen und Aufrufen eines Delegaten .)

Eine nicht delegierte Lösung wäre auch in Ordnung, solange sie die Zielmethode so direkt wie möglich aufruft.

Update: Das funktioniert auch, aber es beruht immer noch auf Reflektion:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Zumindest ist es wahrscheinlicher, dass Probleme bei der Kompilierung auftreten. Aber es zahlt immer noch den Laufzeitpreis für die Reflexion, nicht wahr?

Expertenantwort

Solange Sie anrufen .Compile auf dem Lambda und speichern (und wiederverwenden) den Delegierten, zahlen Sie nur den Reflexionspreis einmal.

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

Um jedoch einen nackten Delegierten zu bekommen nur dieser Methode können Sie verwenden:

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

natürlich sind Sie dann gezwungen, die Konstanten beim Aufrufer anzugeben.

Eine andere Option ist DynamicMethod , aber solange Sie den letzten Delegierten zwischenspeichern, wird dies nicht wesentlich schneller sein. Es bietet mehr Flexibilität (zum Preis der Komplexität), aber das scheint hier nicht der Fall zu sein.




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