2012-04-24 12 views
5

Nuestro sistema de IU puede generar un formulario a partir de un MethodInfo. Antes System.Linq.Expressions, nos iban a dar la MethodInfo utilizando la reflexión (método 1):Mejore el rendimiento de obtener MethodInfo desde MethodCallExpression

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

Lo malo de esto es que si cambiamos el nombre de la firma o InstanceMethod, el código seguiría compilar.

Ingrese expresiones. Ahora hacemos esto (método 2):

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

o este (procedimiento 3):

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

La sintaxis es "mejor", obtenemos IntelliSense, y obtenemos errores de compilación si el método doesn 't existen o la firma no coincide. Sin embargo, el método 2 y el método 3 son de 10 a 20 veces más lentos que la reflexión.

Algunos números (medidos con el cronómetro):

Call individual: Método 1: 0.0000565 Método 2: 0.0004272 Método 3: 0.0019222

100000 Llamadas: Método 1: .1171071 Método 2: 1.5648544 Método 3: 2,0602607

en realidad no compilar la expresión o ejecutarlo, y me interesa si alguien tiene una explicación para la diferencia en el rendimiento .

ACTUALIZACIÓN: El GetMethod <> código:

Método 2:

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

Método 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

preguntando ... ¿lo ha intentado con un delegado personalizado en su lugar? es decir, 'new SomeDelegateType (x.Method)'? –

+0

Por favor, muestre el contenido de GetMethod. Es difícil analizar código que no es visible ... – usr

+0

@MarcGravell, no estoy seguro de entender su pregunta. –

Respuesta

1

Mi conjetura es que es más lento debido a las versiones de expresión hacen la misma reflexión (aunque pueden usar el atajo IL methodof que no tiene análogo en C#) para crear los árboles de expresión, además a la sobrecarga de crear los árboles ellos mismos para cada llamada (no creo que estén en caché por el código que emite el compilador); Además, debes leer esos árboles para recuperar el método.

La reflexión puede ser 'lenta' pero en realidad es bastante rápida; especialmente porque creo que los datos, detrás de escena, también están en la memoria caché. Por lo tanto, una vez que haya llamado al GetMethod, la segunda llamada será más rápida. Esto proporciona otra prueba convincente de por qué las versiones posteriores del árbol de expresiones son más lentas, ya que en realidad están haciendo más trabajo.

Si se siente cómodo con IL, compile una versión con los tres y luego analice la imagen compilada con ILSpy o Reflector (en el modo C# ambos serán inteligentes y reconstituirán el código de expresión en C#, que no es bueno, así que cambie al modo IL) - eche un vistazo al código que se emite para producir los árboles de expresión y verá lo que quiero decir.

Cuestiones relacionadas