用Emit技術替代反射 C# 之 反射性能優化1 從IDataReader中讀取數據實體

  以前在上篇博客說到用表達式來替代反射機制,能夠得到較高的性能提高。這篇咱們來講說用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文件,就像運行代碼,保存代碼並調用編譯器來編譯代碼同樣。大多數狀況下,您須要此功能和此命名空間用於自定義腳本引擎和編譯器。 緩存

Reflection.Emit命名空間有許多可用於重要的的類。如下是兩個最重要的:
  • AssemblyBuilder類是在運行時發出代碼並具備建立動態模塊的方法的任何應用程序的起點。
  • ModuleBuilder類用做在運行時向動態程序集添加類和結構等類型的起點。
  生成MSIL指令的ILGenerator.OpCodes類包括其所需字段中的全部IL指令。MSIL是CLR或中間語言的基本彙編語言的無類型操做代碼。當您編寫任何C#代碼並對其進行編譯時,它將首先轉換爲MSIL。而後,當您在MSIL中調用程序集時,它將以相應的機器語言進行轉換和執行。學習MSIL的最簡單方法是反彙編您編譯的簡單代碼。您可使用.NET SDK實用程序之一ILDasm.exe(IL反彙編程序)在Vs插件庫下載便可,來反彙編任何已編譯的.NET代碼。

 

  本文經過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

C# 之 反射性能優化1

Emit經常使用Opcodes指令使用方法(含實例)

從IDataReader中讀取數據實體性能

相關文章
相關標籤/搜索