2012-04-24 16 views
5

Il nostro sistema di interfaccia utente può generare un modulo da un MethodInfo. Prima System.Linq.Expressions, stavamo ottenendo il MethodInfo utilizzando la riflessione (metodo 1):Migliorare le prestazioni di ottenere MethodInfo da MethodCallExpression

MethodInfo info = typeof(ExtensionTestClass).GetMethod("InstanceMethod", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null); 

La parte brutta di questo è che se abbiamo cambiato la firma o il nome del InstanceMethod, il codice sarebbe ancora la compilazione.

Immettere espressioni. Ora facciamo questo (metodo 2):

MethodInfo info = GetMethod<ExtensionTestClass>(x => x.InstanceMethod("defaultValue", "defaultValue")); 

o questo (metodo 3):

MethodInfo info = GetMethod<ExtensionTestClass, string, string>(x => x.InstanceMethod); 

la sintassi è "meglio", otteniamo intellisense, e otteniamo errori di compilazione se il metodo doesn esiste o la firma non corrisponde. Tuttavia, il metodo 2 e il metodo 3 sono circa da 10 a 20 volte più lenti della riflessione.

Alcuni numeri (misurati con il cronometro):

chiamata singola: Metodo 1: 0,0000,565 mila Metodo 2: 0,0004,272 mila Metodo 3: .0019222

100000 Chiamate: Metodo 1: .1171071 Metodo 2: 1,5648,544 mila Metodo 3: 2,0602607

Noi in realtà non compilare l'espressione o eseguirlo, e mi interessa se qualcuno ha una spiegazione per la differenza di prestazioni .

UPDATE: Il GetMethod <> Codice:

Metodo 2:

public static MethodInfo GetMethod<T>(Expression<Action<T>> target) 
{ 
    MethodCallExpression exp = target.Body as MethodCallExpression; 
    if (exp != null) 
    { 
     return exp.Method; 
    } 
    return null; 
} 

Metodo 3:

public static MethodInfo GetMethod<T, A1, A2>(Expression<Func<T, Action<A1, A2>>> expression) 
{ 
    var lambdaExpression = (LambdaExpression)expression; 
    var unaryExpression = (UnaryExpression)lambdaExpression.Body; 
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 
    var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last(); 
    return (MethodInfo)methodInfoExpression.Value; 
} 
+2

solo per chiedere ... hai provato con un delegato personalizzato, invece? cioè 'new SomeDelegateType (x.Method)'? –

+0

Mostrare il contenuto di GetMethod. È difficile analizzare il codice che non è visibile ... – usr

+0

@MarcGravell, non sono sicuro di aver capito la tua domanda. –

risposta

1

La mia ipotesi è che è più lenta perché le versioni di espressione fanno la stessa riflessione (anche se potrebbero usare la scorciatoia di IL che non ha analoghi in C#) per creare gli alberi di espressione, in aggiunta a per il sovraccarico di creazione degli alberi stessi per ogni chiamata (non penso che siano memorizzati nella cache dal codice emesso dal compilatore); in più devi quindi leggere quegli alberi per far tornare il metodo.

La riflessione potrebbe essere "lenta" ma in realtà è dannatamente veloce; soprattutto dal momento che ritengo che anche i dati, dietro le quinte, siano memorizzati nella cache. Quindi, una volta che hai chiamato GetMethod, la seconda chiamata sarà più veloce. Ciò fornisce un'altra prova convincente del motivo per cui le successive versioni ad albero delle espressioni sono più lente - dal momento che stanno effettivamente facendo più lavoro.

Se si ha familiarità con IL, compilare una versione con tutti e tre e quindi analizzare l'immagine compilata con ILSpy o Reflector (in modalità C# entrambi saranno intelligenti e riconsiderare il codice di espressione in C#, che non è bene, quindi passa alla modalità IL) - dai un'occhiata al codice che viene emesso per produrre gli alberi di espressione e vedrai cosa intendo.

Problemi correlati