博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET(C#):分析IL中的if-else,while和for语句并用Emit实现
阅读量:5050 次
发布时间:2019-06-12

本文共 5981 字,大约阅读时间需要 19 分钟。

这是一篇关于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/YuanZhaoBest/p/5279162.html

你可能感兴趣的文章
【19】AngularJS 应用
查看>>
Spring
查看>>
Linux 系统的/var目录
查看>>
Redis学习---Redis操作之其他操作
查看>>
WebService中的DataSet序列化使用
查看>>
BZOJ 1200 木梳
查看>>
【Linux】【C语言】菜鸟学习日志(一) 一步一步学习在Linxu下测试程序的运行时间...
查看>>
hostname
查看>>
SpringBoot使用其他的Servlet容器
查看>>
关于cookie存取中文乱码问题
查看>>
k8s架构
查看>>
select 向上弹起
查看>>
mysql 多表管理修改
查看>>
group by order by
查看>>
bzoj 5252: [2018多省省队联测]林克卡特树
查看>>
https 学习笔记三
查看>>
Oracle学习之简单查询
查看>>
log4j配置
查看>>
linux 配置SAN存储-IPSAN
查看>>
双链表
查看>>