Emit學習 - OpCodes - 迴圈和異常
阿新 • • 發佈:2020-07-17
本來準備直接進入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 = newDynamicMethod("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 }
以前總是懶, 對於新的知識, 都是看看, 看懂就成, 但是在實際寫的過程中, 會碰到很多看不會遇到的問題, 編碼還真不是看看就能寫好的, 好記性不如爛筆頭, 還是得自己一行一行敲一下, 才能發現裡面的問題.
***********轉摘:https://www.cnblogs.com/elvinle/p/6019238.html