• .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

  • 相关阅读:
    static关键字详解
    解读equals()和hashCode()
    基于马士兵老师的高并发笔记
    scrapy安装及基本使用
    scrapy 简单操作
    python django简单操作
    Map,Filter 和 Reduce
    2017-08-06笔记
    幂等性
    Jmeter jdbc连接
  • 原文地址:https://www.cnblogs.com/yeye518/p/5275097.html
Copyright © 2020-2023  润新知