2010-11-03 13 views
14

Un delegado abierto es un delegado en un método de instancia sin el destino. Para invocarlo, proporciona el objetivo como primer parámetro. Son una manera inteligente de optimizar el código que de otra forma usaría la reflexión y tendría un rendimiento deficiente. Para una introducción para abrir delegados, vea this. La forma en que lo usaría en la práctica es tener un código de reflexión costoso para construir estos delegados abiertos, pero luego podría llamarlos de forma muy económica como una simple llamada de delegado.Creación de un delegado abierto de rendimiento para un creador o definidor de propiedad

Estoy tratando de escribir código que transformará un PropertyInfo arbitrario, en un delegado así para su setter. Hasta ahora se me ocurrió esto:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 

namespace Test 
{ 
    class TestClass 
    { 
     static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property) 
     { 
      MethodInfo setMethod = property.GetSetMethod(); 
      if (setMethod != null && setMethod.GetParameters().Length == 1) //skips over nasty index properties 
      { 
       //To be able to bind to the delegate we have to create a delegate 
       //type like: Action<T,actualType> rather than Action<T,object>. 
       //We use reflection to do that 
       Type setterGenericType = typeof(Action<,>); 
       Type delegateType = setterGenericType.MakeGenericType(new Type[] { typeof(T), property.PropertyType }); 
       var untypedDelegate = Delegate.CreateDelegate(delegateType, setMethod); 

       //we wrap the Action<T,actualType> delegate into an Action<T,object> 
       Action<T, object> setter = (instance, value) => 
       { 
        untypedDelegate.DynamicInvoke(new object[] { instance, value }); 
       }; 
       return setter; 
      } 
      else 
      { 
       return null; 
      } 
     } 

     int TestProp 
     { 
      set 
      { 
       System.Diagnostics.Debug.WriteLine("Called set_TestProp"); 
      } 
     } 

     static void Test() 
     { 
      PropertyInfo property = typeof(TestClass).GetProperty("TestProp"); 
      Action<TestClass, object> setter = MakeSetterDelegate<TestClass>(property); 
      TestClass instance = new TestClass(); 
      setter(instance, 5); 
     } 
    } 
} 

Se escribirá un código similar para el captador. Funciona, pero el delegado del colocador utiliza un DynamicInvoke para convertir de una Acción <derivedType> a la Acción <object>, que sospecho que se está comiendo una buena parte de la optimización que estoy buscando. Entonces las preguntas son:

  1. ¿Es el DynamicInvoke una preocupación real?
  2. ¿Hay alguna manera alrededor?

Respuesta

18

DynamicInvoke no hará un setter de rendimiento. Reflexión sobre un tipo interno genérico es su mejor opción aquí, ya que esto le permitirá usar delegados mecanografiados. Otra opción es DynamicMethod, pero luego debe preocuparse por algunos detalles de IL.

Usted puede que desee ver en HyperDescriptor, que envuelve la IL funciona en una aplicación PropertyDescriptor. Otra opción es la API Expression (si está utilizando .NET 3.5 o superior):

static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property) 
{ 
    MethodInfo setMethod = property.GetSetMethod(); 
    if (setMethod != null && setMethod.GetParameters().Length == 1) 
    { 
     var target = Expression.Parameter(typeof(T)); 
     var value = Expression.Parameter(typeof(object)); 
     var body = Expression.Call(target, setMethod, 
      Expression.Convert(value, property.PropertyType)); 
     return Expression.Lambda<Action<T, object>>(body, target, value) 
      .Compile(); 
    } 
    else 
    { 
     return null; 
    } 
} 

O, alternativamente, con un tipo genérico:

abstract class Setter<T> 
    { 
     public abstract void Set(T obj, object value); 
    } 
    class Setter<TTarget, TValue> : Setter<TTarget> 
    { 
     private readonly Action<TTarget, TValue> del; 
     public Setter(MethodInfo method) 
     { 
      del = (Action<TTarget, TValue>) 
       Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), method); 
     } 
     public override void Set(TTarget obj, object value) 
     { 
      del(obj, (TValue)value); 
     } 

    } 
    static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property) 
    { 
     MethodInfo setMethod = property.GetSetMethod(); 
     if (setMethod != null && setMethod.GetParameters().Length == 1) 
     { 
      Setter<T> untyped = (Setter<T>) Activator.CreateInstance(
       typeof(Setter<,>).MakeGenericType(typeof(T), 
       property.PropertyType), setMethod); 
      return untyped.Set; 
     } 
     else 
     { 
      return null; 
     } 
    } 
+0

Se puede elaborar en su respuesta? 1-¿Qué quiere decir con "Reflexión contra un tipo interno genérico"; 2 - ¿Cómo me ayudaría la API Expression? –

+0

@David - ejemplo de expresión agregado. Voy a batir un ejemplo de tipo interno genérico –

+0

@David - y agregué el ejemplo de tipo genérico interno –

1

vez que hice esta clase. Tal vez esto sirva:

public class GetterSetter<EntityType,propType> 
{ 
    private readonly Func<EntityType, propType> getter; 
    private readonly Action<EntityType, propType> setter; 
    private readonly string propertyName; 
    private readonly Expression<Func<EntityType, propType>> propertyNameExpression; 

    public EntityType Entity { get; set; } 

    public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> property_NameExpression) 
    { 
     Entity = entity; 
     propertyName = GetPropertyName(property_NameExpression); 
     propertyNameExpression = property_NameExpression; 
     //Create Getter 
     getter = propertyNameExpression.Compile(); 
     // Create Setter() 
     MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod(); 
     setter = (Action<EntityType, propType>) 
       Delegate.CreateDelegate(typeof(Action<EntityType, propType>), method); 
    } 


    public propType Value 
    { 
     get 
     { 
      return getter(Entity); 
     } 
     set 
     { 
      setter(Entity, value); 
     } 
    } 

    protected string GetPropertyName(LambdaExpression _propertyNameExpression) 
    { 
     var lambda = _propertyNameExpression as LambdaExpression; 
     MemberExpression memberExpression; 
     if (lambda.Body is UnaryExpression) 
     { 
      var unaryExpression = lambda.Body as UnaryExpression; 
      memberExpression = unaryExpression.Operand as MemberExpression; 
     } 
     else 
     { 
      memberExpression = lambda.Body as MemberExpression; 
     } 
     var propertyInfo = memberExpression.Member as PropertyInfo; 
     return propertyInfo.Name; 
    } 

prueba:

var gs = new GetterSetter<OnOffElement,bool>(new OnOffElement(), item => item.IsOn); 
     gs.Value = true; 
     var result = gs.Value; 
Cuestiones relacionadas