一、概述:
近來也是在看AOP方面的東西,了解到Emit可以實現。之前對Emit的了解也就是停留在Reflector針對方法反編譯出來的部分指令。就用這次機會學習下Emit也用這篇隨筆記錄下學習的過程。某些我也不了解的地方也希望各位了解的朋友指導下。
學習前可以先了解下Opcodes
二、工具
1、vs2015
2、.NET Reflector 9.0
三、入門示例
1、輸出Hello World
C#代碼
static void Main(string[] args) { Console.WriteLine("Hello world!"); }View Code
反編譯獲取到的IL代碼
Emit實現代碼
public void HellowWorld() { //定義Hellow方法沒有返回值沒有參數 DynamicMethod helloWorldMethod = new DynamicMethod("HellowWorld", null, null); //創建IL,動態生成代碼 ILGenerator IL = helloWorldMethod.GetILGenerator(); //將輸出推送到堆棧上 IL.Emit(OpCodes.Ldstr, "Hello World!"); //執行Console.WriteLine IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //方法結束 IL.Emit(OpCodes.Ret); HelloWordDelegate Method = (HelloWordDelegate)helloWorldMethod.CreateDelegate(typeof(HelloWordDelegate)); Method(); }View Code
委托調用過程中。如果有參數會提示“操作可能破壞運行時穩定性”。(沒弄清楚)
四、構建程序集
1、下面通過構建一個類裡面包含兩個方法來做實例
public int Add(int a, int b) { return a + b; } public string AddList(string[] array) { string result = string.Empty; for (int i = 0; i < array.Length; i++) { result = result + array[i]; } return result; }View Code
2、看下兩個方法反編譯出來的IL代碼
下面我們來看看這段IL到底是如何實現的
●L0000到L0009:將string.Empty賦值給自定義變量resultName,加載整數0,L0009跳轉到L001b執行
●L001b到L0027:加載1處索引值,加載參數1(靜態從0開始),Ldlen將數組數目從0開始推送到堆棧上,對比兩個數值的大小,(將對比結果存儲到索引2處,然後再取出(這步實現中可省略)),然後跳轉L_000b執行
●L000b到L001a:加載0處索引值,加載參數1,Ldelem_Ref用來加載string類型元素,執行string.concat方法,將值存儲到索引0處,加載索引1處值,加載整數1,兩值相加,將值存儲到索引0處
●L002a結束返回
3、下面我們通過Emit代碼來實現
public void GenerateAssembly() { string name = "IL.Dynamic"; string fileName = string.Format("{0}.dll", name); //構建程序集 AssemblyName assemblyName = new AssemblyName(name); //應用程序集域 AppDomain domain = AppDomain.CurrentDomain; //實例化一個AssemblyBuilder對象來實現動態程序集的構建 AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); //定義模塊(不加filename為瞬態模塊,不持久) ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name); //定義類型 TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public); //定義一個Add方法進行簡單的相加 MethodBuilder methodBuilder = typeBuilder.DefineMethod("Add", MethodAttributes.Public, typeof(Int32), new Type[] { typeof(int), typeof(int) }); //IL實現 ILGenerator IL = methodBuilder.GetILGenerator(); IL.Emit(OpCodes.Ldarg_1); IL.Emit(OpCodes.Ldarg_2); IL.Emit(OpCodes.Add); IL.Emit(OpCodes.Ret); //定義一個AddList字符串用for拼接方法 MethodBuilder method2Builder = typeBuilder.DefineMethod("AddList", MethodAttributes.Public| MethodAttributes.Static, typeof(string), new Type[] { typeof(string[]) }); FieldBuilder fieldName = typeBuilder.DefineField("resultName", typeof(string), FieldAttributes.Private | FieldAttributes.Static); ILGenerator addIL = method2Builder.GetILGenerator(); //用來保存求和結果的局部變量 LocalBuilder resultStr = addIL.DeclareLocal(typeof(String)); ////循環中使用的局部變量 LocalBuilder i = addIL.DeclareLocal(typeof(Int32)); Label concatLabel = addIL.DefineLabel(); Label LoopLabel = addIL.DefineLabel(); //設置string result = string.Empty; addIL.Emit(OpCodes.Ldsfld, fieldName); addIL.Emit(OpCodes.Stloc_0); //設置i=0 addIL.Emit(OpCodes.Ldc_I4_0); addIL.Emit(OpCodes.Stloc_1); addIL.Emit(OpCodes.Br, concatLabel); //進入循環體 addIL.MarkLabel(LoopLabel); addIL.Emit(OpCodes.Ldloc_0); //參數指定靜態從0開始 addIL.Emit(OpCodes.Ldarg_0); addIL.Emit(OpCodes.Ldloc_1); //Ldelem_Ref用來加載string 類型元素 addIL.Emit(OpCodes.Ldelem_Ref); addIL.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) })); addIL.Emit(OpCodes.Stloc_0); addIL.Emit(OpCodes.Ldloc_1); //i++ addIL.Emit(OpCodes.Ldc_I4_1); addIL.Emit(OpCodes.Add); addIL.Emit(OpCodes.Stloc_1); addIL.MarkLabel(concatLabel); addIL.Emit(OpCodes.Ldloc_1); addIL.Emit(OpCodes.Ldarg_0); addIL.Emit(OpCodes.Ldlen); addIL.Emit(OpCodes.Conv_I4); //Clt比較兩值大小 addIL.Emit(OpCodes.Clt); addIL.Emit(OpCodes.Brtrue_S, LoopLabel); addIL.Emit(OpCodes.Ldloc_0); addIL.Emit(OpCodes.Ret); Type type = typeBuilder.CreateType(); assemblyBuilder.Save(fileName); int[] ints = new int[] { 1, 2, 3, 4 }; string[] array = new string[] { "a", "b", "c" }; object ob = Activator.CreateInstance(type); var result = type.GetMethod("Add").Invoke(ob, new object[] { 8, 9 }); var result1 = type.GetMethod("AddList").Invoke(ob, new object[] { array }); }View Code
3.1、AssemblyBuilderAccess
Run 可以執行但不能保存 Save 保存但不能執行 RunAndSave 可以執行並保存 ReflectionOnly 只反射上下文中加載
4、運行輸出
4.1、生成文件
借助工具反編譯查看IL生成的C#代碼
4.2、運行結果
通過這個實例對IL可以有了比較基礎的了解,在之後的學習中再慢慢喝大家進行交流。項目下載System.IL