2010-07-26 7 views
7

Ist es möglich, einen Ausdrucksbaum zu erstellen, der direkt eine Methode aufruft? Betrachten wir zum Beispiel die folgende Methode:Erstellen eines Ausdrucksbaums, der eine Methode aufruft

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

Ich möchte ein Ausdrucksbaum erzeugen, die MyFunc mit Parametern a = 1 und b = 2 bezeichnet. Eine Möglichkeit, dies zu tun ist mit Reflexion:

var c1 = Expression.Constant(1); 
var c2 = Expression.Constant(2); 
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2); 

Dies ist jedoch von Nachteil, weil Reflexion langsam ist und dann auch was kompilieren Zeit sein sollte Fehler in Laufzeitfehler.

kann ich den folgenden Ansatz verwenden, anstatt:

Expression<Func<int, int, int>> lambda = (a, b) => MyFunc(a, b); 
var expr = Expression.Invoke(lambda, c1, c2); 

Aber das ist immer noch nicht, was ich will, weil es das Verfahren in einem Lambda-Ausdruck hüllt, anstatt es direkt zu nennen.

Eine gute Lösung könnte auf einem Delegierten basieren, wie folgt aus:

Func<int, int, int> del = Program.MyFunc; 
var expr = Expression.Invoke(del, c1, c2); 

Leider, das nicht kompilieren, weil del ein Delegierter ist eher als ein Ausdruck. Gibt es eine Möglichkeit, einen Ausdruck von einem Delegaten zu erstellen? (Beachten Sie, dass ich das Ziel des Delegaten zur Kompilierungszeit kenne, so dass ich die hier beschriebene Flexibilität nicht benötige: Expression Trees and Invoking a Delegate.)

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

Update: Dies funktioniert auch, aber es setzt weiterhin auf Reflexion:

Func<int, int, int> del = Program.MyFunc; 
var expr = Expression.Call(del.Method, c1, c2); 

Mindestens es wahrscheinlicher ist, Probleme bei der Kompilierung-Zeit zu fangen. Aber es zahlt immer noch den Laufzeitpreis für die Reflexion, nicht wahr?

+0

Weitere Informationen über den Anwendungsfall wären nützlich :) z. Mit den Beispielen, die du angegeben hast, kannst du auch eine einfache 'Func ' übergeben ... (und du sagst, dass du das Ziel zur Kompilierzeit kennst). Wozu ist dann der 'Expression'-Baum erforderlich? – porges

+1

Das Kompilieren eines Ausdrucks wird den Overhead der Reflexion in den Schatten stellen. Da Sie die Reflexion nur einmal ausführen, wenn Sie den Ausdruck machen, werden die Kosten vernachlässigbar sein. – Gabe

Antwort

11

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

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 ** 

, jedoch ein nackt Delegierten nur diese Methode zu erhalten, können Sie verwenden:

var method = typeof(Program).GetMethod("MyFunc"); 
Func<int, int, int> func = (Func<int, int, int>) Delegate.CreateDelegate(
     typeof(Func<int, int, int>), method); 

natürlich Sie die Konstanten zur Verfügung zu stellen bei dem Anrufer dann gezwungen sind.

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

Verwandte Themen