这是一篇关于IL和反射Emit的文章(所以不喜欢IL或者Emit的就没必要往下看了),要求读者对IL和Emit工作原理较了解。所有分析IL均在Visual Studio 2010 SP1下编译生成。(其他编译器不一定100%结果一样但逻辑上肯定是等价的,希望读者学到“为什么”,而不是“是什么”)。

返回目录

分析if-else

C#中的if-else:

  1. if (条件)
    //为true代码
    else
    //为false代码

if-else的IL执行逻辑:

  1. 条件为真:goto

    //false代码
    goto 退出

    //true代码
    退出

来分析一个示例程序:

C#:

  1. static void doo(bool b)
    {
    if(b)
    Console.WriteLine(1);
    else
    Console.WriteLine(2);
    }

IL:

  1. .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语句的动态方法,比如这样一个方法:

  1. static void test(bool b)
    {
    if(b)
    Console.WriteLine("真");
    else
    Console.WriteLine("假");
    }

使用反射Emit创建并运行,代码:

  1. //+ using System.Reflection;
    //+ using System.Reflection.Emit;
    static void Main(string[] args)
    {
    //创建DynamicMethod对象
    var dm = GetIfElse();
  2.  
  3. //测试
    dm.Invoke(null, new object[] { true });
    dm.Invoke(null, new object[] { false });
    }
  4.  
  5. static DynamicMethod GetIfElse()
    {
    var dm = new DynamicMethod("", null, new Type[] { typeof(bool) });
    var gen = dm.GetILGenerator();
  6.  
  7. Label lbFalse = gen.DefineLabel();
    Label lbRet = gen.DefineLabel();
  8.  
  9. //判断
    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);
  10.  
  11. return dm;
    }

输出:


OK!

返回目录

分析while

C#中的while:

  1. while (条件)
    {
    //true代码
    }

IL中while执行逻辑:

  1. goto 判断

    //true代码
    判断
    条件为真:goto

分析代码,C#:

  1. static void doo(bool b)
    {
    while (b)
    {
    Console.WriteLine("TRUE");
    }
    }

IL:

  1. .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循环就非常简单了,我们将动态创建这样一个方法:

  1. static void test(bool b)
    {
    if(b)
    Console.WriteLine("TRUE");
    }

代码:

  1. //+ using System.Reflection;
    //+ using System.Reflection.Emit;
    static void Main(string[] args)
    {
    //创建DynamicMethod对象
    var dm = GetWhile();
  2.  
  3. //测试
    dm.Invoke(null, new object[] { false });
    Console.WriteLine("参数false,结束,等待3秒后运行参数true");
    System.Threading.Thread.Sleep(3000);
    dm.Invoke(null, new object[] { true });
    }
  4.  
  5. static DynamicMethod GetWhile()
    {
    var dm = new DynamicMethod("", null, new Type[] { typeof(bool) });
    var gen = dm.GetILGenerator();
  6.  
  7. Label lbCondition = gen.DefineLabel();
    Label lbTrue = gen.DefineLabel();
  8.  
  9. //跳至判断
    gen.Emit(OpCodes.Br, lbCondition);
  10.  
  11. //标记True代码
    gen.MarkLabel(lbTrue);
  12.  
  13. gen.EmitWriteLine("TRUE");
  14.  
  15. //标记判断代码
    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);
  16.  
  17. gen.Emit(OpCodes.Ret);
    return dm;
    }

程序输出,当然false参数没有任何输出,然后等3秒后,无限输出TRUE

  1. 参数false,结束,等待3秒后运行参数true
    TRUE
    TRUE
    TRUE
    TRUE
    ...

返回目录

分析for

C#中的for

  1. for (定义; 判断; 追加动作)
    {
    //动作
    }

IL中for执行逻辑:

  1. goto 判断
    动作
    追加动作
  2.  
  3. 判断
    条件为真:goto 动作

呵呵,看似略复杂的for循环其实也是很简单的。

分析代码:

  1. for (int i = 0; i < 10; i++)
    {
    Console.WriteLine(i);
    }

IL:

  1. .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动态方法

我们将动态创建这样一个方法:

  1. static void test(int c)
    {
    for (int i = 0; i < c; i++)
    {
    Console.WriteLine(i);
    }
    }

代码:

  1. //+ using System.Reflection;
    //+ using System.Reflection.Emit;
    static void Main(string[] args)
    {
    //创建DynamicMethod对象
    var dm = GetFor();
  2.  
  3. //测试
    dm.Invoke(null, new object[] { 3 });
    }
  4.  
  5. static DynamicMethod GetFor()
    {
    var dm = new DynamicMethod("", null, new Type[] { typeof(int) });
    var gen = dm.GetILGenerator();
  6.  
  7. //临时变量i
    LocalBuilder locI = gen.DeclareLocal(typeof(int));
    Label lbCondition = gen.DefineLabel();
    Label lbTrue = gen.DefineLabel();
  8.  
  9. //i=0
    gen.Emit(OpCodes.Ldc_I4_0);
    gen.Emit(OpCodes.Stloc, locI);
  10.  
  11. //跳至判断
    gen.Emit(OpCodes.Br, lbCondition);
  12.  
  13. //标记True代码
    gen.MarkLabel(lbTrue);
  14.  
  15. //Console.WriteLine(i)
    gen.Emit(OpCodes.Ldloc, locI);
    gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }));
  16.  
  17. //追加代码
    //i++
    gen.Emit(OpCodes.Ldloc, locI);
    gen.Emit(OpCodes.Ldc_I4_1);
    gen.Emit(OpCodes.Add);
    gen.Emit(OpCodes.Stloc, locI);
  18.  
  19. //判断代码
    gen.MarkLabel(lbCondition);
    gen.Emit(OpCodes.Ldloc, locI);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Clt);
    //如果True,跳至true代码
    gen.Emit(OpCodes.Brtrue, lbTrue);
  20.  
  21. gen.Emit(OpCodes.Ret);
    return dm;
    }

输出:

  1. 0
    1
    2

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

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

  1. 通过IL分析C#中的委托、事件、Func、Action、Predicate之间的区别与联系

    先说一下个人理解的结论吧: delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类. delegate声明的变量与delegate声明的事件,并没有本质的区别,事件是在dele ...

  2. 【Win 10 应用开发】分析 URI 中的查询字符串

    分析URI中的字符有K种方法(K >= 2),如果查询字符串中的参数比较简单,可以通过子字符串查找的方式来处理:如果查询字符串相对复杂,你可以使用正则表达式来匹配 key1=value1 ,  ...

  3. IL中的栈和闪电的Owin推荐

    最近几天有幸得到闪电大哥的指点,了解了EMIT和IL中的一些指令.虽然有高射炮打蚊子的说法,但是我相信“二八定律”,80%的功能可以用20%的技术解决,20%的功能只能用80%的技术解决.大哥的博客: ...

  4. 【吐血推荐】简要分析unity3d中剪不断理还乱的yield

    在学习unity3d的时候很容易看到下面这个例子: void Start () { StartCoroutine(Destroy()); } IEnumerator Destroy(){ yield ...

  5. [转]DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)

    在CSDN中发现这篇文章,讲解的比较详细,所以在这里备份一个.原文链接:http://blog.csdn.net/breaksoftware/article/details/8167641 DllMa ...

  6. 一个使用C#的TPL Dataflow Library的例子:分析文本文件中词频

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:一个使用C#的TPL Dataflow Library的例子:分析文本文件中词频.

  7. 从虚拟机指令执行的角度分析JAVA中多态的实现原理

    从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧 ...

  8. Linux的nmon监控结果分析文件中网络分析NET

    1.首先,使用# ifconfig查看Linux系统中的网卡名称,有的是eth0,有的是em1,以查看结果为准,下图为em1 2.先试试Linux系统中有没有安装ethtool工具,没有的话,下载et ...

  9. 【Java入门提高篇】Day23 Java容器类详解(六)HashMap源码分析(中)

    上一篇中对HashMap中的基本内容做了详细的介绍,解析了其中的get和put方法,想必大家对于HashMap也有了更好的认识,本篇将从了算法的角度,来分析HashMap中的那些函数. HashCod ...

随机推荐

  1. java通过jsp的Excel导出

    在项目中一般导出报表用poi,可是假设你不想用框架就用简单的jsp也能够实现报表导出.并且实现起来还特别简单. 先看一下效果截图: 点击导出后的效果截图: 详细实现: 第一:在页面的列表页面中就是普通 ...

  2. 关于C语言中的inline

    在c中,为了解决一些频繁调用的小函数大量消耗栈空间或是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数.栈空间就是指放置程式的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的 ...

  3. hdu 1234

    Problem Description 每天第一个到机房的人要把门打开,最后一个离开的人要把门关好.现有一堆杂乱的机房签 到.签离记录,请根据记录找出当天开门和关门的人. Input 测试输入的第一行 ...

  4. 不同频率下的pwm配置

    200k //PWM1 PWMPERDL1=0xb3; PWMPERDH1= 0x00; PWMCCNTL1=0x6B; PWMCCNTH1= ; PWMDBDY1=0x2B; //死区延时计时器 / ...

  5. Gora官方范例

    参考官方文档:http://gora.apache.org/current/tutorial.html 项目代码见:https://code.csdn.net/jediael_lu/mygoradem ...

  6. Android provider authorities冲突

    在Android项目中的AndroidManifest中provider的authorities相当于一个系统级的全局变量它的值必须唯一,如果有两个应用的authorities相同会导致安装失败.

  7. AT89C 系列单片机解密原理

    单片机解密简单就是擦除单片机片内的加密锁定位.由于AT89C系列单片机擦除操作时序设计上的不合理.使在擦除片内程序之前首先擦除加密锁定位成为可能.AT89C系列单片机擦除操作的时序为:擦除开始---- ...

  8. 下一代云计算模式:Docker正掀起个性化商业革命

    作者: 吴宁川  来源: ITValue  发布时间: 2015-09-20 10:41  阅读: 10008 次  推荐: 16   原文链接   [收藏] 文/ITValue 记者吴宁川 从 20 ...

  9. CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)的内部网络结构有什么区别?

    https://www.zhihu.com/question/34681168 CNN(卷积神经网络).RNN(循环神经网络).DNN(深度神经网络)的内部网络结构有什么区别?修改 CNN(卷积神经网 ...

  10. jquery UI推荐

    Bootstrap http://www.bootcss.com/ http://www.ligerui.com/ http://j-ui.com/#demo_page2 http://jqueryu ...