2010-11-03 1 views
14

Một đại biểu mở là một đại biểu cho một phương pháp thể hiện mà không có đích. Để gọi nó, bạn cung cấp các mục tiêu như là tham số đầu tiên của nó. Họ là một cách thông minh để tối ưu hóa mã mà nếu không sẽ sử dụng sự phản chiếu và có hiệu suất kém. Để có giới thiệu cho các đại biểu mở, hãy xem this. Cách bạn sẽ sử dụng nó trong thực tế là phải có mã phản chiếu đắt tiền để xây dựng các đại biểu mở này, nhưng sau đó bạn sẽ có thể gọi chúng rất rẻ như một cuộc gọi Đại biểu đơn giản.Tạo một đại diện mở có hiệu suất cho một trình thiết lập thuộc tính hoặc getter

Tôi đang cố gắng viết mã sẽ biến đổi một PropertyInfo tùy ý, thành một đại biểu cho trình thiết lập của nó. Cho đến nay tôi đã đưa ra điều này:

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); 
     } 
    } 
} 

Mã tương tự sẽ được viết cho getter. Nó hoạt động, nhưng các đại biểu setter sử dụng một DynamicInvoke để chuyển đổi từ một hành động <derivedType> sang hành động <object>, mà tôi nghi ngờ là ăn một phần tốt của tối ưu hóa tôi sau. Vì vậy, các câu hỏi là:

  1. DynamicInvoke có phải là mối quan ngại thực sự không?
  2. Có cách nào xung quanh nó không?

Trả lời

18

DynamicInvoke sẽ không thực hiện thiết lập trình diễn. Phản ánh đối với loại nội bộ chung chung là lựa chọn tốt hơn của bạn ở đây, vì điều này sẽ cho phép bạn sử dụng đã nhập đại biểu. Một tùy chọn khác là DynamicMethod, nhưng sau đó bạn cần phải lo lắng về một vài chi tiết IL.

Bạn có thể muốn xem HyperDescriptor, bao gồm IL hoạt động thành triển khai PropertyDescriptor. Một lựa chọn khác là Expression API (nếu bạn đang sử dụng .NET 3.5 trở lên):

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; 
    } 
} 

Hoặc cách khác với một kiểu generic:

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

Bạn có thể xây dựng trên câu trả lời của bạn? 1-Ý bạn là gì bởi "Phản chiếu chống lại một loại bên trong chung chung"; 2 - API Expression sẽ giúp tôi như thế nào? –

+0

@David - ví dụ về biểu thức được thêm vào. Tôi sẽ roi lên một ví dụ loại bên trong chung chung –

+0

@David - và thêm ví dụ kiểu chung bên trong –

1

khi tôi làm lớp này. Có lẽ nó giúp:

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; 
    } 

kiểm tra:

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