一、概述:
近來也是在看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