C#性能優化黑科技(一)【反射】修改屬性的快速方法

當我們在擼碼的時候,不可避免的要使用反射來修改屬性的值。我們都知道這種反射是太陽一般的慢,如果屬性類型是值類型,那麼就會因爲裝箱拆箱產生額外的GC(垃圾回收)。那麼加快速度並減少GC呢,本文將介紹一個名爲“CreateDelegate”的黑科技。


設我們有這麼一個類:

public class Test
{
	public int id { get; private set;}
}

那麼當我們要在類型的外部修改id值的時候,該怎麼辦呢?當然是把set前面的private去掉,本文終。某些時候,我們因爲限制而無法修改類型的內容(例如使用第三方庫,再例如爲了保證編程規範), 那麼我們就需要這麼寫:

      //For test  
        Test a = new Test ();  
        Test b = new Test ();  
        Type type = typeof(Test);  
        PropertyInfo pi = type.GetProperty ("id");    
        pi.SetValue (a, -1, null);  
        pi.SetValue (b, -2, null);

SetValue方法的第三個參數是爲了索引器準備的,我們先不管。

第一個參數是該類型(Test)的對象(a或b)。

第二個參數就是我們將要設置的值,類型是object。

這TM就很尷尬了,我們都知道,int類型轉object會進行裝箱操作,而真正賦值的時候又會進行拆箱操作,一大波GC正在襲來。

而且這個方法慢,非常慢,出了名的慢,因爲在真正賦值之前,會進行一系列的類型判斷。

那麼,如何解決這些問題?我們就需要使用黑科技——Delegate.CreateDelegate,代碼很簡單:

      var act = Delegate.CreateDelegate (typeof(Action<Test, int>), pi.GetSetMethod (true)) as Action<Test, int>;  
        act (a, 1);  
        act (b, 2);


stackoverflow上有更完整的解決方案(https://stackoverflow.com/questions/6158768/c-sharp-reflection-fastest-way-to-update-a-property-value/16082916#16082916)。

順手貼上代碼:

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;  
}
public class Accessor<S>  
{  
    public static Accessor<S, T> Create<T>(Expression<Func<S, T>> memberSelector)  
    {  
        return new GetterSetter<T>(memberSelector);  
    }  
  
    public Accessor<S, T> Get<T>(Expression<Func<S, T>> memberSelector)  
    {  
        return Create(memberSelector);  
    }  
  
    public Accessor()  
    {  
  
    }  
  
    class GetterSetter<T> : Accessor<S, T>  
    {  
        public GetterSetter(Expression<Func<S, T>> memberSelector) : base(memberSelector)  
        {  
  
        }  
    }  
}  
  
public class Accessor<S, T> : Accessor<S>  
{  
    Func<S, T> Getter;  
    Action<S, T> Setter;  
  
    public bool IsReadable { get; private set; }  
    public bool IsWritable { get; private set; }  
    public T this[S instance]  
    {  
        get  
        {  
            if (!IsReadable)  
                throw new ArgumentException("Property get method not found.");  
  
            return Getter(instance);  
        }  
        set  
        {  
            if (!IsWritable)  
                throw new ArgumentException("Property set method not found.");  
  
            Setter(instance, value);  
        }  
    }  
  
    protected Accessor(Expression<Func<S, T>> memberSelector) //access not given to outside world  
    {  
        var prop = memberSelector.GetPropertyInfo();  
        IsReadable = prop.CanRead;  
        IsWritable = prop.CanWrite;  
        AssignDelegate(IsReadable, ref Getter, prop.GetGetMethod());  
        AssignDelegate(IsWritable, ref Setter, prop.GetSetMethod());  
    }  
  
    void AssignDelegate<K>(bool assignable, ref K assignee, MethodInfo assignor) where K : class  
    {  
        if (assignable)  
            assignee = assignor.CreateDelegate<K>();  
    }  
}




那麼最後再來一道課後題:

如果我們只能得到類型的Type,並不能直接使用類型,那該怎麼辦呢?

(我已經得到了一個不算很成熟的解決方案,有緣我再拿出來討論吧。)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章