.NET(C#):分析IL中的if-else,while和for语句并用Emit实现

这是一篇关于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

Open-mouthed smile

转自:https://www.mgenware.com/blog/?p=90

原文地址:https://www.cnblogs.com/yeye518/p/5275097.html