2008-11-03 8 views
15

Wenn eine Expression<T> kompiliert wird, wird der resultierende Code implizit vom Framework zwischengespeichert? Ich denke dabei an die statischen Methoden Regex, bei denen das Framework implizit die letzten Regexes kompiliert und zwischenspeichert.Wenn ein Ausdruck <T> kompiliert wird, wird er implizit zwischengespeichert?

Wenn kompiliert Expression<T> Objekte nicht gecached sind, können Sie empfehlen einige Best Practices für die Führung der Compile-Zeit oder Fallstricke, die Probleme verursachen könnte, wenn ich manuell einen Ausdruck cachen?

public MyResultType DoSomething(int arg1, int arg2) 
{ 
    var result = invokeHandler(
     (IDoSomethingHandler h) => h.DoSomething(arg1, arg2) 
    ); 
    return result; 
} 

private TResult invokeHandler<T, TResult>(Expression<Func<T, TResult>> action) 
    where T : class 
{ 
    // Here, I might want to check to see if action is already cached. 

    var compiledAction = action.Compile(); 
    var methodCallExpr = action as MethodCallExpression; 

    // Here, I might want to store methodCallExpr in a cache somewhere. 

    var handler = ServiceLocator.Current.GetInstance<T>(); 
    var result = compiledAction(handler); 

    return result; 
} 

In diesem Beispiel bin ich etwas besorgt, dass, wenn ich den kompilierten Ausdruck zwischenzuspeichern, dass sie die Werte von arg1 und arg2, wie sie zu der Zeit waren der Ausdruck kompiliert wurde verwenden, anstatt diese Werte abrufen von der entsprechenden Stelle im Stapel (dh anstatt die aktuellen Werte zu erhalten).

Antwort

10

Nein; Ich glaube nicht, dass es so ist; Wenn Sie es im Cache speichern möchten, müssen Sie die Delegate Referenz (normalerweise Func<...> oder Action<...>) halten. Wenn Sie die bestmögliche Leistung erzielen möchten, würden Sie dies auch als parametrisierten Ausdruck kompilieren, sodass Sie beim Aufruf unterschiedliche Werte senden können.

In diesem Fall Re-Formulierung würde helfen:

public MyResultType DoSomething(int arg1, int arg2) 
{ 
    var result = invokeHandler(
     (IDoSomethingHandler h, int a1, int a2) => h.DoSomething(a1, a2), 
     arg1, arg2); 
    return result; 
} 

private TResult invokeHandler<T, TResult>(Expression<Func<T,int,int,TResult>> action, 
    int arg1, int arg2) 
    where T : class 
{ 
    // Here, I might want to check to see if action is already cached. 

    var compiledAction = action.Compile(); 
    var methodCallExpr = action as MethodCallExpression; 

    // Here, I might want to store methodCallExpr in a cache somewhere. 

    var handler = ServiceLocator.Current.GetInstance<T>(); 
    var result = compiledAction(handler, arg1, arg2); 

    return result; 
} 

das heißt die Zahlen Parameter des Ausdrucks machen, und übergeben Sie die tatsächlich diejenigen es zur Laufzeit (und nicht als Konstanten im Ausdruck).

+1

Hallo Mark, ich habe noch nie solche Optimierungen getroffen. Können Sie uns bitte mitteilen, ob dies in Bezug auf Performance, Zwischenspeicherung vorkompilierter Abfragen wirklich hilfreich ist? –

1

Lambda-Expersionen werden nicht automatisch zwischengespeichert. Sie müssen dafür eigene Caching/Memoization-Algorithmen implementieren. Überprüfen Sie die zugehörige Frage Stackoverflow:

Is it possible to cache a value evaluated in a lambda expression?

Es ist wichtig, dass die Lambda-Ausdrücke faul in C# ausgewertet werden zu beachten.