反射學習入門篇 (三)--Emit的使用


   在之前的兩篇文章中,了解了普通的反射技術使用方法及一些常用操作,結尾提到使用反射的性能問題,反射當中遇到的性能問題有很多種優化的解決方案,其中一種就是使用反射發出,即Emit技術的使用。

     首先了解下Emit能做什么,MSDN上對Reflection.Emit是這樣定義的:System.Reflection.Emit 命名空間包含允許編譯器或工具發出元數據和Microsoft 中間語言 (MSIL)並可選擇在磁盤上生成 PE 文件的類。直白的說就是通過Emit的使用,我們可以在程序中動態的去創建類型,方法,模塊等到,在創建之后我們可以選擇一次性使用,也可以把它保存下來,持久的使用。因此在我們需要動態創建類型,而又不希望永久駐留內存的話,我們就可以在創建類型后,動態編譯一次,使用完畢之后就不用再保留。因此在性能上有很大的優勢。了解了Emit能做什么,也就可以明白Emit的使用場景了,通常我們在下面幾種情形時可以選擇使用Emit來實現: 

     1.  運行中動態的創建類型、模塊等,同時又需要提高效率(可以動態編譯一次,然后就不用再處理了).

     2 .延遲綁定對象的使用。

     3 . 工具插件及IDE的開發等。 

     4.   ORM的實現。

     5.   減少反射的性能損失。 

 

  使用Emit常用的幾個類如下:

類名 用途
AssemblyBuilder  用來定義和創建動態的程序集
ConstructorBuilder  用來創建動態類的構造函數
CustomAttributeBuilder   用來創建用戶自定義的特性
MethodBuilder        用來創建動態類的方法,也可創建構造函數,因為構造函數本身也是一個特殊的方法
ModuleBuilder    用來創建動態程序集中的模塊
TypeBuilder    用來定義和創建動態類的新實例
DynamicMethod   用來創建可動態編譯和執行的動態方法
ILGenerator         用來生成中間語言,即MSIL指令
OpCodes  提供 Microsoft 中間語言 (MSIL) 指令的字段表示形式

 

 

 

 

 

   

 

   

 

 

首先來看下使用Emit的一般步驟: 

    1. 創建一個程序集。 

    2. 在程序集內創建一個模塊。 

    3. 在模塊內創建動態類。 

    4. 為動態類添加動態方法,屬性,事件,等等。 

    5. 生成相關的IL代碼。

    6. 返回創建的類型或是持久化保存到硬盤中。    

 

接下來就通過一個實例來看下Emit技術的使用:   

   

View Code
 1             //得到當前的應用程序域
 2             AppDomain appDm = AppDomain.CurrentDomain;
 3             //初始化AssemblyName的一個實例
 4             AssemblyName an = new AssemblyName();
 5             //設置程序集的名稱
 6             an.Name = "EmitLearn";
 7             //動態的在當前應用程序域創建一個應用程序集
 8             AssemblyBuilder ab = appDm.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
 9             //動態在程序集內創建一個模塊
10             ModuleBuilder mb = ab.DefineDynamicModule("EmitLearn");
11             //動態的在模塊內創建一個類
12             TypeBuilder tb = mb.DefineType("HelloEmit", TypeAttributes.Public | TypeAttributes.Class);
13             //動態的為類里創建一個方法
14             MethodBuilder mdb = tb.DefineMethod("SayHelloEmit", MethodAttributes.Public, null, new Type[] { typeof(string) });
15 
16             //得到該方法的ILGenerator
17             ILGenerator ilG = mdb.GetILGenerator();
18             //加載傳入方法的參數到堆棧
19             ilG.Emit(OpCodes.Ldarg_1);
20             //調用Console.WriteLine方法,輸出傳入的字符
21             ilG.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
22             ilG.Emit(OpCodes.Ret);
23             //創建類的Type對象
24             Type tp = tb.CreateType();
25             //實例化一個類
26             object ob = Activator.CreateInstance(tp);
27             //得到類中的方法,通過Invoke來觸發方法的調用..
28             MethodInfo mdi = tp.GetMethod("SayHelloEmit");
29             mdi.Invoke(ob, new object[] { "HelloEmit" });

 

  上面的代碼簡單的展示了Emit的使用方法,然而在最后的方法調用上其實用了傳統的反射Invoke方法,這一點在性能上還是存在損耗的,其實這里也可以通過Emit來實現,提高執行效率,這一點兒在下一篇文章中再繼續探討,堅持,每天進步一點點! 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM