2012-05-30 15 views
21

Tengo problemas de rendimiento con el uso de la reflexión.
Así que decidieron crear delegados de las propiedades de los objetos de mi y hasta ahora tiene esto:Rendimiento de reflexión - Crear delegado (Propiedades C#)

TestClass cwp = new TestClass(); 
var propertyInt = typeof(TestClass).GetProperties().Single(obj => obj.Name == "AnyValue"); 
var access = BuildGetAccessor(propertyInt.GetGetMethod()); 
var result = access(cwp); 
static Func<object, object> BuildGetAccessor(MethodInfo method) 
{ 
    var obj = Expression.Parameter(typeof(object), "o"); 

    Expression<Func<object, object>> expr = 
     Expression.Lambda<Func<object, object>>(
      Expression.Convert(
       Expression.Call(
        Expression.Convert(obj, method.DeclaringType), 
        method), 
       typeof(object)), 
      obj); 

    return expr.Compile(); 
} 

Los resultados fueron altamente satisfactorios, alrededor de 30-40 veces más rápido que con el método convencional (PropertyInfo.GetValue (obj, null);)

El problema es: ¿Cómo puedo hacer un SetValue de una propiedad, que funciona de la misma manera? Lamentablemente, no obtuve una manera.

Lo estoy haciendo porque no puedo usar métodos con <T> debido a la estructura de mi aplicación.

+0

"Lo estoy haciendo porque no puedo usar métodos con" < T > "debido a la estructura de mi aplicación" - ¿Eso significa su versión de NETFX <2.0? ¿Por qué no puedes usar genéricos en tu aplicación? –

+0

Además, ¿qué tiene que ver la creación de delegados para sus propiedades con la reflexión y qué problema está tratando de resolver mediante la reflexión? –

+0

Los delegados tienen un rendimiento mucho mejor y se pueden usar dinámicamente. Son la opción preferida cuando necesita usar una invocación dinámica. – GregRos

Respuesta

15

Esto debería funcionar para usted:

static Action<object, object> BuildSetAccessor(MethodInfo method) 
{ 
    var obj = Expression.Parameter(typeof(object), "o"); 
    var value = Expression.Parameter(typeof(object)); 

    Expression<Action<object, object>> expr = 
     Expression.Lambda<Action<object, object>>(
      Expression.Call(
       Expression.Convert(obj, method.DeclaringType), 
       method, 
       Expression.Convert(value, method.GetParameters()[0].ParameterType)), 
      obj, 
      value); 

    return expr.Compile(); 
} 

de uso:

var accessor = BuildSetAccessor(typeof(TestClass).GetProperty("MyProperty").GetSetMethod()); 
var instance = new TestClass(); 
accessor(instance, "foo"); 
Console.WriteLine(instance.MyProperty); 

Con TestClass:

public class TestClass 
{ 
    public string MyProperty { get; set; } 
} 

Imprime:

foo

2

Utilice los tipos dinámicos. Usan reflejo debajo del capó, pero son mucho más rápido.

De lo contrario ...

hay toneladas de bibliotecas libres reflexión más rápido por ahí con las licencias permisivas. Te vincularía, pero hay demasiados, y no estoy seguro de cuál te conviene. Simplemente busque codeplex, etc. Cuando encuentre algo que le guste, pruébelo.

Pero sí, quizás antes de eso, piense si la reflexión realmente es la respuesta. A menudo hay otras soluciones.

Edit: Como solicitada ...

http://geekswithblogs.net/SunnyCoder/archive/2009/06/26/c-4.0-dynamics-vs.-reflection.aspx
http://theburningmonk.com/2010/09/performance-test-dynamic-method-invocation-in-csharp-4/
http://www.mssoftwareconsulting.com/msswc/blog/post/C-40-and-dynamic-performance.aspx

Es de conocimiento común en la medida de lo que puedo decir.

+0

¿Lo siento? ¿Por qué el voto negativo? – GregRos

+1

Dynamics no debe usarse para reflexión, IMO, a menos que no haya ** otra forma **. ¿Puedes publicar algunos datos que admitan * que son mucho más rápidos *? ... y un ejemplo de uso de dinámica de una manera que las solicitudes OP? Estas pueden ser algunas de las razones de los votos negativos ... – IAbstract

+0

Está usando la invocación dinámica. P.ej. establecer métodos, propiedades, etc. Eso es ** exactamente ** lo que se supone que debes hacer con la dinámica. – GregRos

10

Creo que sería mejor que CreateDelegate construir si el rendimiento es la clave. Dado que conoce la firma del método de antemano, que aquí es solo GetGetMethod y GetSetMethod del PropertyInfo, puede crear un delegado para ejecutar directamente el mismo método con la misma firma. Las expresiones serían más adecuadas si necesita construir alguna lógica (para la cual no tenía un método maneja) para los delegados.He hecho un poco de evaluación comparativa de las diferentes rutas a este problema:

Func<S, T> Getter; 
Action<S, T> Setter; 
PropertyInfo Property; 
public void Initialize(Expression<Func<S, T>> propertySelector) 
{ 
    var body = propertySelector.Body as MemberExpression; 
    if (body == null) 
     throw new MissingMemberException("something went wrong"); 

    Property = body.Member as PropertyInfo; 



    //approaches: 

    //Getter = s => (T)Property.GetValue(s, null); 

    //Getter = memberSelector.Compile(); 

    //ParameterExpression inst = Expression.Parameter(typeof(S)); 
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Property(inst, Property), inst).Compile(); 

    //var inst = Expression.Parameter(typeof(S)); 
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Call(inst, Property.GetGetMethod()), inst).Compile(); 

    //Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), Property.GetGetMethod()); 



    //Setter = (s, t) => Property.SetValue(s, t, null); 

    //var val = Expression.Parameter(typeof(T)); 
    //var inst = Expression.Parameter(typeof(S)); 
    //Setter = Expression.Lambda<Action<S, T>>(Expression.Call(inst, Property.GetSetMethod(), val), 
    //           inst, val).Compile(); 

    //Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), Property.GetSetMethod()); 
} 


//Actual calls (tested under loop): 
public T Get(S instance) 
{ 
    //direct invocation: 
    //return (T)Property.GetValue(instance, null); 

    //calling the delegate: 
    //return Getter(instance); 
} 
public void Set(S instance, T value) 
{ 
    //direct invocation: 
    //Property.SetValue(instance, value, null); 

    //calling the delegate: 
    //Setter(instance, value); 
} 

Resultados para unos 10 millones de llamadas - (Get, Set):

GetValue-SetValue (directa): 3800 m, 5500 ms

GetValue-FijarValor (delegado): 3600 ms, 5300 ms

expresiones compilados:

Get: Expression.Property: 280 ms 

     Expression.Call: 280 ms 

     direct compile: 280 ms 
    Set: 300 ms 

crear delegado: 130 ms, 135 ms

llamada propiedad directa: 70 ms, 70 ms

lo haría, en su caso, escriba:

public static Func<S, T> BuildGetAccessor<S, T>(Expression<Func<S, T>> propertySelector) 
{ 
    return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate<Func<S, T>>(); 
} 

public static Action<S, T> BuildSetAccessor<S, T>(Expression<Func<S, T>> propertySelector) 
{ 
    return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate<Action<S, T>>(); 
} 

// a generic extension for CreateDelegate 
public static T CreateDelegate<T>(this MethodInfo method) where T : class 
{ 
    return Delegate.CreateDelegate(typeof(T), method) as T; 
} 

public static PropertyInfo GetPropertyInfo<S, T>(this Expression<Func<S, T>> propertySelector) 
{ 
    var body = propertySelector.Body as MemberExpression; 
    if (body == null) 
     throw new MissingMemberException("something went wrong"); 

    return body.Member as PropertyInfo; 
} 

Así que ahora usted llama:

TestClass cwp = new TestClass(); 
var access = BuildGetAccessor((TestClass t) => t.AnyValue); 
var result = access(cwp); 

No es eso simpl er? Había escrito una clase genérica here para manejar la situación exacta.

Cuestiones relacionadas