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

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來實現,提高執行效率,這一點兒在下一篇文章中再繼續探討,堅持,每天進步一點點!