• Emit学习(3)


    本来准备直接进入Dapper的, 但是昨天初步看了一下, 内容不少, Dapper不愧是一款成熟的框架, 里面对各种情况的考虑, 很实用, 不过这也使得我短时间内看不完, 所以得等几天了. 

    那就先来看看循环和异常吧, 看IL跳转的时候, 有一个标签, 跳来跳去的, 在OpCodes里面, 也是有对应的实现的.

    一、示例

    public static void Xunhuan(int num)
            {
                try
                {
                    int sum = 0;
                    if (num < 1)
                    {
                        throw new Exception("num is less than 1");
                    }
                    for (int i = 1; i <= num; i++)
                    {
                        sum += i;
                    }
                    Console.WriteLine("executed successfully : Sum = " + sum);
                }
                catch (Exception e)
                {
                    Console.WriteLine("error happened : " + e.Message);
                }
            }

    在调用Xunhuan()的时候, 传入小于1的值时, 会抛出异常, 进入异常处理部分,  否则进入循环(此处为了演示, 使用循环, 否则应该使用算法计算结果), 求和. 结果我就不贴了.

    二、OpCodes实现

    static Action<int> action;
            static void Main(string[] args)
            {
                //定义一个动态方法
                var method = new DynamicMethod("Xunhuan", null, new Type[] { typeof(int) });
                ILGenerator IL = method.GetILGenerator();
    
                //定义标签
                var label1 = IL.DefineLabel();  
                var xunLabel = IL.DefineLabel();
                var endLabel = IL.DefineLabel();
    
                //定义本地变量
                var sum = IL.DeclareLocal(typeof(int));
                var i = IL.DeclareLocal(typeof(int));
    
                IL.Emit(OpCodes.Ldc_I4_0);
                IL.Emit(OpCodes.Stloc_0); //sum = 0
    
                IL.Emit(OpCodes.Ldc_I4_1);
                IL.Emit(OpCodes.Stloc_1); // i = 1
                IL.Emit(OpCodes.Ldstr, "enter number : num = ");
                IL.Emit(OpCodes.Ldarg_0);
                //实现方式一 装箱
                IL.Emit(OpCodes.Box, typeof(int));   //装箱
                IL.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(object) }));
                IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
    
                Label tryLabel = IL.BeginExceptionBlock(); //try
                IL.Emit(OpCodes.Ldarg_0);  //num
                IL.Emit(OpCodes.Ldc_I4_1); // 1
                IL.Emit(OpCodes.Bge, label1); //num >= 1 -> label1, 注: try里面的跳转, 不能跳出try语句, 只能在内部
                
                IL.Emit(OpCodes.Ldstr, "num is less than 1");
                IL.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) })); //new Exception();
                IL.Emit(OpCodes.Throw); //throw
    
                IL.MarkLabel(label1);
                IL.Emit(OpCodes.Ldloc_1); //i
                IL.Emit(OpCodes.Ldarg_0); //num
                IL.Emit(OpCodes.Bgt, xunLabel); // i > num -> endLabel
    
                IL.Emit(OpCodes.Ldloc_0);
                IL.Emit(OpCodes.Ldloc_1);
                IL.Emit(OpCodes.Add);
                IL.Emit(OpCodes.Stloc_0); // sum += i;
    
                IL.Emit(OpCodes.Ldloc_1);
                IL.Emit(OpCodes.Ldc_I4_1);
                IL.Emit(OpCodes.Add);
                IL.Emit(OpCodes.Stloc_1); //i+=1;
                IL.Emit(OpCodes.Br_S, label1);
    
                IL.MarkLabel(xunLabel);
                IL.Emit(OpCodes.Ldstr, "executed successfully : Sum = ");
                IL.Emit(OpCodes.Ldloc_0);
                //实现方式二 调用Convert.ToString(int num)方法
                IL.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(int) }));
                IL.Emit(OpCodes.Call, typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }));
                IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }));
    
                IL.MarkLabel(endLabel);
                IL.Emit(OpCodes.Nop);
    
                IL.BeginCatchBlock(typeof(Exception)); //catch
                IL.Emit(OpCodes.Callvirt, typeof(Exception).GetMethod("get_Message"));
                IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
                IL.EndExceptionBlock(); //end
              
                IL.Emit(OpCodes.Ret);
    
                action = (Action<int>)method.CreateDelegate(typeof(Action<int>)); //此处通过委托的方式来调动方法, 而不再是保存成dll文件了
    
                Console.WriteLine("-------------正常执行---------------");
                action.Invoke(10);
                Console.WriteLine("-------------异常执行---------------");
                action.Invoke(-10);
                Console.ReadKey();
            }

    实现的方式并不是唯一的, 我写的这个方法, 并不是最简的. 下面贴出reflector反编译的IL代码, 与我写的这个还是有不少不同的.

       .method public hidebysig static void Xunhuan(int32 num) cil managed
        {
            .maxstack 2
            .locals init (
                [0] int32 num2,
                [1] int32 num3,
                [2] class [mscorlib]System.Exception exception,
                [3] bool flag)
            L_0000: nop 
            L_0001: nop 
            L_0002: ldc.i4.0 
            L_0003: stloc.0 
            L_0004: ldarg.0 
            L_0005: ldc.i4.1 
            L_0006: clt 
            L_0008: ldc.i4.0 
            L_0009: ceq 
            L_000b: stloc.3 
            L_000c: ldloc.3 
            L_000d: brtrue.s L_001b
            L_000f: nop 
            L_0010: ldstr "num is less than 1"
            L_0015: newobj instance void [mscorlib]System.Exception::.ctor(string)
            L_001a: throw 
            L_001b: ldc.i4.1 
            L_001c: stloc.1 
            L_001d: br.s L_0029
            L_001f: nop 
            L_0020: ldloc.0 
            L_0021: ldloc.1 
            L_0022: add 
            L_0023: stloc.0 
            L_0024: nop 
            L_0025: ldloc.1 
            L_0026: ldc.i4.1 
            L_0027: add 
            L_0028: stloc.1 
            L_0029: ldloc.1 
            L_002a: ldarg.0 
            L_002b: cgt 
            L_002d: ldc.i4.0 
            L_002e: ceq 
            L_0030: stloc.3 
            L_0031: ldloc.3 
            L_0032: brtrue.s L_001f
            L_0034: ldstr "executed successfully : Sum = "
            L_0039: ldloc.0 
            L_003a: box int32
            L_003f: call string [mscorlib]System.String::Concat(object, object)
            L_0044: call void [mscorlib]System.Console::WriteLine(string)
            L_0049: nop 
            L_004a: nop 
            L_004b: leave.s L_0068
            L_004d: stloc.2 
            L_004e: nop 
            L_004f: ldstr "error happened : "
            L_0054: ldloc.2 
            L_0055: callvirt instance string [mscorlib]System.Exception::get_Message()
            L_005a: call string [mscorlib]System.String::Concat(string, string)
            L_005f: call void [mscorlib]System.Console::WriteLine(string)
            L_0064: nop 
            L_0065: nop 
            L_0066: leave.s L_0068
            L_0068: nop 
            L_0069: ret 
            .try L_0001 to L_004d catch [mscorlib]System.Exception handler L_004d to L_0068
        }

     以前总是懒, 对于新的知识, 都是看看, 看懂就成, 但是在实际写的过程中, 会碰到很多看不会遇到的问题, 编码还真不是看看就能写好的, 好记性不如烂笔头, 还是得自己一行一行敲一下, 才能发现里面的问题.

  • 相关阅读:
    python之路day10命名空间和作用域、函数嵌套,作用域链、闭包
    20200719 千锤百炼软工人第十四天
    20200720 千锤百炼软工人第十五 天
    JavaScript中基本数据类型和引用数据类型的区别
    js 经纬度坐标格式化 度分秒转度 度转度分秒
    export ,export default 和 import 区别 以及用法
    10月28日学习日志
    10月20日学习日志
    10月29日学习日志
    10月27日学习日志
  • 原文地址:https://www.cnblogs.com/elvinle/p/6019238.html
Copyright © 2020-2023  润新知