这是一篇关于IL和反射Emit的文章(所以不喜欢IL或者Emit的就没必要往下看了),要求读者对IL和Emit工作原理较了解。所有分析IL均在Visual Studio 2010 SP1下编译生成。(其他编译器不一定100%结果一样但逻辑上肯定是等价的,希望读者学到“为什么”,而不是“是什么”)。
目录
分析if-else
C#中的if-else:
if (条件) //为true代码 else //为false代码
if-else的IL执行逻辑:
条件为真:goto 真 假 //false代码 goto 退出 真 //true代码 退出
来分析一个示例程序:
C#:
static void doo(bool b) { if(b) Console.WriteLine(1); else Console.WriteLine(2); }
IL:
.maxstack 2 .locals init ( [0] bool CS$4$0000) L_0000: nop L_0001: ldarg.0 //b进栈 L_0002: ldc.i4.0 //0(false)进栈 L_0003: ceq //比较b和false L_0005: stloc.0 //将结果赋值给临时bool L_0006: ldloc.0 //加载bool L_0007: brtrue.s L_0012 //如果false,跳至L_0012 L_0009: ldc.i4.1 //true代码 L_000a: call void [mscorlib]System.Console::WriteLine(int32) L_000f: nop L_0010: br.s L_0019 //跳至返回 L_0012: ldc.i4.2 //false代码 L_0013: call void [mscorlib]System.Console::WriteLine(int32) L_0018: nop L_0019: ret //返回
Emit创建if-else动态方法
有了上面的知识,就可以自己创建一个有if-else语句的动态方法,比如这样一个方法:
static void test(bool b) { if(b) Console.WriteLine("真"); else Console.WriteLine("假"); }
使用反射Emit创建并运行,代码:
//+ using System.Reflection; //+ using System.Reflection.Emit; static void Main(string[] args) { //创建DynamicMethod对象 var dm = GetIfElse(); //测试 dm.Invoke(null, new object[] { true }); dm.Invoke(null, new object[] { false }); } static DynamicMethod GetIfElse() { var dm = new DynamicMethod("", null, new Type[] { typeof(bool) }); var gen = dm.GetILGenerator(); Label lbFalse = gen.DefineLabel(); Label lbRet = gen.DefineLabel(); //判断 gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldc_I4, 1); gen.Emit(OpCodes.Ceq); //如果false: 跳至false代码 gen.Emit(OpCodes.Brfalse, lbFalse); //true代码 gen.EmitWriteLine("真"); //跳至退出 gen.Emit(OpCodes.Br, lbRet); //false代码 gen.MarkLabel(lbFalse); gen.EmitWriteLine("假"); //退出代码 gen.MarkLabel(lbRet); gen.Emit(OpCodes.Ret); return dm; }
输出:
真 假
OK!
分析while
C#中的while:
while (条件) { //true代码 }
IL中while执行逻辑:
goto 判断 真 //true代码 判断 条件为真:goto 真
分析代码,C#:
static void doo(bool b) { while (b) { Console.WriteLine("TRUE"); } }
IL:
.maxstack 1 .locals init ( [0] bool CS$4$0000) L_0000: nop L_0001: br.s L_0010 //跳至判断 L_0003: nop //true代码 L_0004: ldstr "TRUE" L_0009: call void [mscorlib]System.Console::WriteLine(string) L_000e: nop L_000f: nop L_0010: ldarg.0 //开始判断,载入b L_0011: stloc.0 L_0012: ldloc.0 //将b的值赋予临时变量 L_0013: brtrue.s L_0003 //如果为true,跳至true代码 L_0015: ret
Emit创建while动态方法
上面懂了的话,创建一个while循环就非常简单了,我们将动态创建这样一个方法:
static void test(bool b) { if(b) Console.WriteLine("TRUE"); }
代码:
//+ using System.Reflection; //+ using System.Reflection.Emit; static void Main(string[] args) { //创建DynamicMethod对象 var dm = GetWhile(); //测试 dm.Invoke(null, new object[] { false }); Console.WriteLine("参数false,结束,等待3秒后运行参数true"); System.Threading.Thread.Sleep(3000); dm.Invoke(null, new object[] { true }); } static DynamicMethod GetWhile() { var dm = new DynamicMethod("", null, new Type[] { typeof(bool) }); var gen = dm.GetILGenerator(); Label lbCondition = gen.DefineLabel(); Label lbTrue = gen.DefineLabel(); //跳至判断 gen.Emit(OpCodes.Br, lbCondition); //标记True代码 gen.MarkLabel(lbTrue); gen.EmitWriteLine("TRUE"); //标记判断代码 gen.MarkLabel(lbCondition); //判断 gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldc_I4, 1); gen.Emit(OpCodes.Ceq); //如果True,跳至true代码 gen.Emit(OpCodes.Brtrue, lbTrue); gen.Emit(OpCodes.Ret); return dm; }
程序输出,当然false参数没有任何输出,然后等3秒后,无限输出TRUE
参数false,结束,等待3秒后运行参数true TRUE TRUE TRUE TRUE ...
分析for
C#中的for
for (定义; 判断; 追加动作) { //动作 }
IL中for执行逻辑:
goto 判断 动作 追加动作 判断 条件为真:goto 动作
呵呵,看似略复杂的for循环其实也是很简单的。
分析代码:
for (int i = 0; i < 10; i++) { Console.WriteLine(i); }
IL:
.maxstack 2 .locals init ( [0] int32 i, [1] bool CS$4$0000) L_0000: nop L_0001: ldc.i4.0 L_0002: stloc.0 //i=0 L_0003: br.s L_0012 //跳至判断 L_0005: nop //true代码 L_0006: ldloc.0 //动作代码 L_0007: call void [mscorlib]System.Console::WriteLine(int32) L_000c: nop //动作代码结束 L_000d: nop //追加动作代码 L_000e: ldloc.0 //i++ L_000f: ldc.i4.1 L_0010: add L_0011: stloc.0 //更新i,追加动作代码结束 L_0012: ldloc.0 //判断代码 L_0013: ldc.i4.s 10 //10进栈 L_0015: clt //<=判断指令,判断i是否小于10 L_0017: stloc.1 L_0018: ldloc.1 //将结果存入临时本地bool变量 L_0019: brtrue.s L_0005 //如果为true,跳至true代码 L_001b: ret
Emit创建for动态方法
我们将动态创建这样一个方法:
static void test(int c) { for (int i = 0; i < c; i++) { Console.WriteLine(i); } }
代码:
//+ using System.Reflection; //+ using System.Reflection.Emit; static void Main(string[] args) { //创建DynamicMethod对象 var dm = GetFor(); //测试 dm.Invoke(null, new object[] { 3 }); } static DynamicMethod GetFor() { var dm = new DynamicMethod("", null, new Type[] { typeof(int) }); var gen = dm.GetILGenerator(); //临时变量i LocalBuilder locI = gen.DeclareLocal(typeof(int)); Label lbCondition = gen.DefineLabel(); Label lbTrue = gen.DefineLabel(); //i=0 gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Stloc, locI); //跳至判断 gen.Emit(OpCodes.Br, lbCondition); //标记True代码 gen.MarkLabel(lbTrue); //Console.WriteLine(i) gen.Emit(OpCodes.Ldloc, locI); gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) })); //追加代码 //i++ gen.Emit(OpCodes.Ldloc, locI); gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Add); gen.Emit(OpCodes.Stloc, locI); //判断代码 gen.MarkLabel(lbCondition); gen.Emit(OpCodes.Ldloc, locI); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Clt); //如果True,跳至true代码 gen.Emit(OpCodes.Brtrue, lbTrue); gen.Emit(OpCodes.Ret); return dm; }
输出:
0 1 2
转自:https://www.mgenware.com/blog/?p=90