1)本篇博客並不是原創,而是我針對.NET反射相關知識的總結。本篇內容來源彙總於3篇博客。在後面的介紹中會在開頭給出對應的連接,方便讀者自行學習。
2)本篇博客主要針對表達式樹代碼進行詳細講解。html
咱們知道反射與直接調用相比性能要慢不少,所以本篇主要針對如何對反射進行優化。 目前最多見的優化反射性能的方法就是採用委託:用委託的方式調用須要反射調用的方法(或者屬性、字段)。編程
那麼如何獲得委託? 三種方法:Emit、Delegate.CreateDelegate、ExpressionTree 。下面將分別介紹這3中方法。app
Emit建立委託,主要靠本身編寫IL代碼。操做IL代碼:難度高,並且不易於閱讀。因此我對這種方法並不感冒。下面將直接給出代碼,在代碼中有對應的註釋,各位有興趣能夠研究研究。ide
public class EmitToReflector { public delegate void SetValueDelegate(object target, object arg); public static SetValueDelegate CreatePropertySetter(PropertyInfo property) { //驗證
if (property == null) throw new ArgumentNullException("property"); if (!property.CanWrite) return null; MethodInfo setMethod = property.GetSetMethod(true); //建立一個名爲PropertySetter的動態方法,其目的就是生成上面的setMethod
DynamicMethod dm = new DynamicMethod("PropertySetter", null,new Type[] { typeof(object), typeof(object) },property.DeclaringType, true); //建立一個MSIL生成器,爲動態方法生成代碼
ILGenerator il = dm.GetILGenerator(); //下面全是操做IL
if (!setMethod.IsStatic) { il.Emit(OpCodes.Ldarg_0); } il.Emit(OpCodes.Ldarg_1); EmitCastToReference(il, property.PropertyType); if (!setMethod.IsStatic && !property.DeclaringType.IsValueType) { il.EmitCall(OpCodes.Callvirt, setMethod, null); } else il.EmitCall(OpCodes.Call, setMethod, null); //方法結束,返回
il.Emit(OpCodes.Ret); //生成對應的委託
return (SetValueDelegate)dm.CreateDelegate(typeof(SetValueDelegate)); } private static void EmitCastToReference(ILGenerator il, Type type) { if (type.IsValueType) il.Emit(OpCodes.Unbox_Any, type); else il.Emit(OpCodes.Castclass, type); } }
運行結果:(測試代碼我會在文章末尾給出)函數
後來我瞭解到,Emit其強大之處在於:能夠動態的生成程序集、方法,而後對其進行調用,實現AOP的編程思想。(這個我沒了解過,前文只是拋磚引玉之功用)性能
原文連接:http://www.cnblogs.com/yingql/archive/2009/03/20/1418007.html學習
Delegate.CreateDelegate()也能夠建立委託,可是它有一個缺點,就是隻能建立參數是強類型(<T>)委託,而不能建立參數是通用類型(Object)委託。
測試
下面的測試代碼,說明了Delegate.CreateDelegate()沒法建立參數是通用類型的委託。優化
Person person = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); //只能建立強類型委託 //Action<Person, string> setter = (Action<Person, string>)Delegate.CreateDelegate( // typeof(Action<Person, string>), null, propInfo.GetSetMethod()); //放開這段代碼,你會發現便宜錯誤
Action<object, object> setter = (Action<object, object>)Delegate.CreateDelegate( typeof(Action<object, object>), null, propInfo.GetSetMethod()); setter(person, "Test"); Console.WriteLine(person.Name);
針對反射,及程序運行時獲得對應的Type(若是知道具體的類型,又何須用反射呢?),而後對其進行操做。因此支持參數是通用類型是必須的。spa
解決具體類型-》通用類型的轉換分如下幾個步驟
1.具體類型-》泛型,及Action<Person,string>-》Action<TTarget,TValue>。
public class DelegateToReflector<TTarget, TValue> { Action<TTarget, TValue> _setter; public DelegateToReflector(PropertyInfo propertyInfo) { if (propertyInfo == null) throw new ArgumentNullException("propertyInfo"); if (propertyInfo.CanWrite == false) throw new NotSupportedException("屬性不支持寫操做。"); MethodInfo m = propertyInfo.GetSetMethod(true); _setter = (Action<TTarget, TValue>)Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), null, m); } }
2.生成賦值委託時,根據入參的屬性(Property),沒法直接生成Action<TTarget,TValue>。因此在①中把Action<TTarget,TValue>做爲類的一個變量。經過構造函數對其進行賦值。這樣在②中能夠經過Type.MakeGenericType()獲取對應的類型,而後建立對應的實例,從而爲Action<TTarget,TValue>賦值。
public class GetterSetterFactory { public static object CreatePropertySetterWrapper(PropertyInfo propertyInfo) { if (propertyInfo == null) throw new ArgumentNullException("propertyInfo"); if (propertyInfo.CanWrite == false) throw new NotSupportedException("屬性不支持寫操做。"); MethodInfo mi = propertyInfo.GetSetMethod(true); if (mi.GetParameters().Length > 1) throw new NotSupportedException("不支持構造索引器屬性的委託。"); Type instanceType = typeof(DelegateToReflector<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType); return Activator.CreateInstance(instanceType, propertyInfo); } }
3.經過①、②獲得可用的Action<TTarget,TValue>(setter)後。下面就是調用。調用將以通用的形式(setter(object,object))的形式調用。能夠定義一個接口
public interface ISetValue { void Set(object target, object val); } public class DelegateToReflector<TTarget, TValue> : ISetValue { //...以前的代碼
public void Set(object target, object value) { _setter((TTarget)target, (TValue)value); } } public class GetterSetterFactory { public static ISetValue CreatePropertySetterWrapper(PropertyInfo propertyInfo) { //...以前的代碼
return (ISetValue)Activator.CreateInstance(instanceType, propertyInfo); } }
4.最後就是測試代碼
static void TestDelegateToReflector() { int count = 1000000; Person testObj = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); Console.Write("泛型委託花費時間: "); DelegateToReflector<Person, string> setter3 = new DelegateToReflector<Person, string>(propInfo); Stopwatch watch4 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter3.Set(testObj, "Test"); watch4.Stop(); Console.WriteLine(watch4.Elapsed.ToString()); Console.Write("通用接口花費時間: "); var setter4 = GetterSetterFactory.CreatePropertySetterWrapper(propInfo); Stopwatch watch5 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter4.Set(testObj, "Test"); watch5.Stop(); Console.WriteLine(watch5.Elapsed.ToString()); }
結論:Delegate.CreateDelegate建立委託,代碼可讀性Emit建立委託好太多,可是實現上有一些繞(因爲沒法直接映射Action<object,object>,因此須要繞了一個大圈子來解決這個問題),不夠直接。
原文連接:http://www.cnblogs.com/fish-li/archive/2013/02/18/2916253.html
表達式樹建立委託,實現的思路上比Delgate.CreateDelagate直接,代碼的實現上也易於閱讀。依舊以SetProperty()做爲示例,Delegate.CreateDelegate()沒法直接建立Action<object,object>,而表達式樹能夠直接建立Action<object,object>。
public static Action<object, object> InitSetter(PropertyInfo propertyInfo) { //((T)instance).Property = ((V)parm) //定義兩個參數:instance(屬性的擁有者),parm(要設置的值) var instance = Expression.Parameter(typeof(object), "instance"); var parm = Expression.Parameter(typeof(object), "parm"); //判斷方法是不是靜態的,若非靜態方法,須要把instance轉換爲對應的類型 var instanceCast = propertyInfo.GetSetMethod().IsStatic ? null : Expression.Convert(instance, propertyInfo.ReflectedType); //參數類型轉換((V)parm) var parmCast = Expression.Convert(parm, propertyInfo.PropertyType); //((T)instance).Property var property = Expression.Property(instanceCast, propertyInfo); //屬性賦值操做 ((T)instance).Property = ((V)parm) var setProperty = Expression.Assign(property, parmCast); //定義委託 var lambda = Expression.Lambda<Action<object, object>>(setProperty, instance, parm); //建立委託 return lambda.Compile(); }
說明:
1.Expression有許多靜態方法,你須要自行了解。
2.代碼已經給出對應的註釋,我想不用我多做贅述了。
3.注意這個方法的第一行註釋,就是這個方法的實現思路。
4.表達式樹須要作對應的類型轉換,這點必定要切記。
最後就是測試代碼:
static void TestExpressionToReflector() { int count = 1000000; Person testObj = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); Console.Write("泛型委託花費時間: "); DelegateToReflector<Person, string> setter3 = new DelegateToReflector<Person, string>(propInfo); Stopwatch watch4 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter3.Set(testObj, "Test"); watch4.Stop(); Console.WriteLine(watch4.Elapsed.ToString()); Console.Write("通用接口花費時間: "); var setter4 = ExpressionToReflector.InitSetter(propInfo); Stopwatch watch5 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter4(testObj, "Test"); watch5.Stop(); Console.WriteLine(watch5.Elapsed.ToString()); }
原文連接:http://www.cnblogs.com/JeffreyZhao/archive/2008/11/24/1338682.html
結束語:優化反射,通常都是經過建立委託來對其進行優化。三種生成委託的方法,我我的最喜歡錶達式樹,理由:直接。思路直接,實現直接。其實處理反射,還有一種運行時處理方法---dynamic。最後,感謝你們的耐心閱讀。測試代碼下載