以前在上篇博客說到用表達式來替代反射機制,能夠得到較高的性能提高。這篇咱們來講說用Emit技術來替代反射。html
System.Reflection.Emit命名空間類可用於動態發出Microsoft中間語言(MSIL)代碼,以便生成的代碼能夠直接執行。反射也用於獲取有關類及其成員的信息。換句話說,反射是一種技術,容許您檢查描述類型及其成員的元數據,你可能以編程方式訪問過組件對象模型類型庫, .NET中的反射很是類似,但功能強大且易於使用。使用.NET編譯器編譯源文件時,編譯器會產生源文件中語句中的MSIL代碼以及描述文件中定義的類型的元數據。正是這個元數據,.NET中的反射API使你可以檢查。在這個System.Reflection命名空間中,有一些類可用於幫助訪問程序中固有的結構,好比類、類型、字段、結構、枚舉、成員和方法。例如,您使用Type類來標識所反映的類的類型,FieldInfo類表示結構或枚舉的字段。MemberInfo類表示反射類的成員,並使用MethodInfo類表示反射類的方法。PrimeRealFipe類表示反射類中的方法的參數。編程
使用System.Reflection.Emit命名空間類在能夠編譯時建立代碼,但前提是必須懂IL代碼。(本文不作IL代碼詳解,由於我也不會。。。)事實上,你實際編寫的是就是幕後的MSIL自己。你可使用反射在內存中定義程序集,爲該程序集建立類/模塊,而後爲該模塊建立其餘模塊成員和新類型。你一樣也可使用Emit來構造程序集。Reflection.Emit是一個強大的命名空間,咱們能夠在運行時動態地發出瞬態和持久化程序集。Reflection.Emit產生一個低級,語言中立的MSIL。一般,咱們經過將源代碼保存到磁盤而後編譯該源代碼來建立程序集,而後咱們調用咱們須要從該程序集中使用的類的方法,該程序集是在磁盤上編譯的。可是你能夠想象,這涉及額外的磁盤寫入和讀取工做!使用反射生成代碼,咱們能夠省略此開銷並當即將操做代碼直接發送到內存中。反射發射只不過是直接在代碼中編寫任何彙編代碼,而後即時調用生成的代碼。這也並非說反射效率就是高,由於在運行期產生指令也是須要時間,各有優缺點。api
System.Reflection.Emit命名空間提供用戶動態建立.exe文件所需的類。它的類容許編譯器或工具發出元數據和MSIL。所以,您能夠動態地在磁盤上建立.exe文件,就像運行代碼,保存代碼並調用編譯器來編譯代碼同樣。大多數狀況下,您須要此功能和此命名空間用於自定義腳本引擎和編譯器。 緩存
本文經過Emit技術來提升後期綁定對象的性能,儘管您不能像硬綁定那樣快速執行調用,但執行效果會比在運行時產生代碼在綁定更好。代碼基本與前篇博客用lambda表達式樹替代反射基本同樣,核心代碼替換過來便可,以下:性能優化
public class PropertyEmit { private PropertySetterEmit setter; private PropertyGetterEmit getter; public String PropertyName { get; private set; } public PropertyInfo Info { get; private set; } public PropertyEmit(PropertyInfo propertyInfo) { if (propertyInfo == null) { throw new ArgumentNullException("屬性不能爲空"); } if (propertyInfo.CanWrite) { setter = new PropertySetterEmit(propertyInfo); } if (propertyInfo.CanRead) { getter = new PropertyGetterEmit(propertyInfo); } this.PropertyName = propertyInfo.Name; this.Info = propertyInfo; } /// <summary> /// 屬性賦值操做(Emit技術) /// </summary> /// <param name="instance"></param> /// <param name="value"></param> public void SetValue(Object instance,Object value) { this.setter?.Invoke(instance, value); } /// <summary> /// 屬性取值操做(Emit技術) /// </summary> /// <param name="instance"></param> /// <returns></returns> public Object GetValue(Object instance) { return this.getter?.Invoke(instance); } private static readonly ConcurrentDictionary<Type, PropertyEmit[]> securityCache = new ConcurrentDictionary<Type, PropertyEmit[]>(); /// <summary> /// 獲取對象屬性 /// </summary> /// <param name="type">對象類型</param> /// <returns></returns> public static PropertyEmit[] GetProperties(Type type) { return securityCache.GetOrAdd(type, t => t.GetProperties().Select(p => new PropertyEmit(p)).ToArray()); } } /// <summary> /// Emit 動態構造 Get方法 /// </summary> public class PropertyGetterEmit { private readonly Func<Object, Object> getter; public PropertyGetterEmit(PropertyInfo propertyInfo) { //Objcet value = Obj.GetValue(Object instance); if (propertyInfo == null) { throw new ArgumentNullException("propertyInfo"); } this.getter = CreateGetterEmit(propertyInfo); } public Object Invoke(Object instance) { return getter?.Invoke(instance); } private Func<Object, Object> CreateGetterEmit(PropertyInfo property) { if (property == null) throw new ArgumentNullException("property"); MethodInfo getMethod = property.GetGetMethod(true); DynamicMethod dm = new DynamicMethod("PropertyGetter", typeof(Object), new Type[] { typeof(Object) }, property.DeclaringType, true); ILGenerator il = dm.GetILGenerator(); if (!getMethod.IsStatic) { il.Emit(OpCodes.Ldarg_0); il.EmitCall(OpCodes.Callvirt, getMethod, null); } else il.EmitCall(OpCodes.Call, getMethod, null); if (property.PropertyType.IsValueType) il.Emit(OpCodes.Box, property.PropertyType); il.Emit(OpCodes.Ret); return (Func<Object, Object>)dm.CreateDelegate(typeof(Func<Object, Object>)); } } /// <summary> /// Emit動態構造Set方法 /// </summary> public class PropertySetterEmit { private readonly Action<Object, Object> setFunc; public PropertySetterEmit(PropertyInfo propertyInfo) { //Obj.Set(Object instance,Object value) if (propertyInfo == null) { throw new ArgumentNullException("propertyInfo"); } this.setFunc = CreatePropertySetter(propertyInfo); } private Action<Object, Object> CreatePropertySetter(PropertyInfo property) { if (property == null) throw new ArgumentNullException("property"); MethodInfo setMethod = property.GetSetMethod(true); DynamicMethod dm = new DynamicMethod("PropertySetter", null, new Type[] { typeof(Object), typeof(Object) }, property.DeclaringType, true); ILGenerator il = dm.GetILGenerator(); 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 (Action<Object, Object>)dm.CreateDelegate(typeof(Action<Object, Object>)); } private static void EmitCastToReference(ILGenerator il, Type type) { if (type.IsValueType) il.Emit(OpCodes.Unbox_Any, type); else il.Emit(OpCodes.Castclass, type); } public void Invoke(Object instance,Object value) { this.setFunc?.Invoke(instance, value); } }
與表達式一塊兒對比,其測試代碼以下:工具
Student student = new Student(); //學生對象,裏面有一個Name屬性 PropertyInfo propertyInfo = student.GetType().GetProperty(nameof(student.Name)); Property PropertyExp = new Property(propertyInfo); PropertyEmit propertyEmit = new PropertyEmit(propertyInfo); Int32 loopCount = 1000000; //執行次數 CodeTimer.Initialize(); //測試環境初始化 CodeTimer.Time("基礎反射", loopCount, () => { propertyInfo.SetValue(student, "Fode",null); }); CodeTimer.Time("lambda表達式樹", loopCount, () => { PropertyExp.SetValue(student, "Fode"); }); CodeTimer.Time("Emit",loopCount,()=> { propertyEmit.SetValue(student, "Fode"); }); CodeTimer.Time("直接賦值", loopCount, () => { student.Name = "Fode"; }); Console.ReadKey();
測試效果圖以下:表達式與Emit速度基本相同,將我上述的方法CreatePropertySetter改爲靜態會比表達式快一點。在使用的過程當中,最好將其封裝成一個靜態泛型類緩存起來,一直new PropertyEmit這個對象反而效率會很低。代碼下載。oop
文章結尾在分享幾個我認爲寫得不錯,可能對你們有幫助的文章:post