2008-12-06 8 views
12

Ich habe eine Ausdrucksbaumstruktur, die ich erstellt habe, indem ich ein Xml unter Verwendung der Ausdrucksklasse in C# analysiert habe. See this question.Wie kompiliere ich einen Ausdrucksbaum in eine aufrufbare Methode, C#?

Ich habe nur hinzufügen, subtrahieren, teilen, multiplizieren, Parameter und und in meinem Ausdrucksbaum. Gibt es eine Möglichkeit, diesen ExpressionTree in eine aufrufbare Methode zu konvertieren? ... oder muss ich die IL manuell ausgeben?

Mit freundlichen Grüßen

+2

Weitere Beispiele hinzugefügt ... –

Antwort

10

Hier ist ein Beispiel für beide Ansätze. Wenn ich etwas verpasst habe oder weitere Informationen wünschen, lass es mich wissen.

static void Main() 
{ 
    // try to do "x + (3 * x)" 

    var single = BuildSingle<decimal>(); 
    var composite = BuildComposite<decimal>(); 

    Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M)); 
} 
// utility method to get the 3 as the correct type, since there is not always a "int x T" 
static Expression ConvertConstant<TSource, TDestination>(TSource value) 
{ 
    return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination)); 
} 
// option 1: a single expression tree; this is the most efficient 
static Func<T,T> BuildSingle<T>() 
{   
    var param = Expression.Parameter(typeof(T), "x"); 
    Expression body = Expression.Add(param, Expression.Multiply(
     ConvertConstant<int, T>(3), param)); 
    var lambda = Expression.Lambda<Func<T, T>>(body, param); 
    return lambda.Compile(); 
} 
// option 2: nested expression trees: 
static Func<T, T> BuildComposite<T>() 
{ 

    // step 1: do the multiply: 
    var paramInner = Expression.Parameter(typeof(T), "inner"); 
    Expression bodyInner = Expression.Multiply(
     ConvertConstant<int, T>(3), paramInner); 
    var lambdaInner = Expression.Lambda(bodyInner, paramInner); 

    // step 2: do the add, invoking the existing tree 
    var paramOuter = Expression.Parameter(typeof(T), "outer"); 
    Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter)); 
    var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter); 

    return lambdaOuter.Compile(); 
} 

Persönlich würde ich auf die erste Methode zielen; es ist sowohl einfacher als auch effizienter. Dies kann beinhalten, dass der ursprüngliche Parameter durch einen Stapel von verschachteltem Code weitergegeben wird, aber sei es so. Ich habe irgendwo Code, der den "Invoke" -Ansatz (Composite) verwendet, und schreibt den Baum als ersten Ansatz (single) neu - aber es ist ziemlich komplex und lang. Aber sehr nützlich für Entity Framework (die Expression.Invoke nicht unterstützt).

+0

Das hat den Trick ... Vielen Dank. –

12

Sie benötigen einen Lambda zu schaffen - das heißt

var lambda = Expression.Lambda<Func<float,int>>(body, param); 
Func<float,int> method = lambda.Compile(); 
int v = method(1.0); // test 

wo "Körper" ist der Ausdruck Baum (ein Schwimmer nehmen, ein int zurückkehrt) Beteiligung des ParameterExpression param.

Sie könnten auch this und this hilfreich finden.

+0

Das Problem ist ((x + 2) + y)/z Wenn mehr als ein Parameter Teil der verschiedenen Sub-Ausdrücke in der Struktur, was mache ich? –

+0

Mehrere Parameter sind möglich (es ist ein params Array von ParameterExpression); Für Unterausdrücke müssen Sie den inneren Ausdruck aufrufen (Expression.Invoke?) –

+0

Sie können den gesamten Baum auch in einen einzelnen Ausdruck erstellen; Das ist etwas effizienter, aber komplexer. –