C# 异常处理(Catch Throw)IL分析

时间:2021-12-26 03:24:49

1、catch throw的几种形式及性能影响:

private void Form1_Click(object sender, EventArgs e) { try { } catch { throw; } } private void Form1_Load(object sender, EventArgs e) { try { } catch (Exception) { throw; } } private void Form1_Enter(object sender, EventArgs e) { try { } catch (Exception ee) { throw; } } private void Form1_DoubleClick(object sender, EventArgs e) { try { } catch (Exception ee) { throw ee; } }        对应的IL代码(以下代码是release版本的IL代码):.method private hidebysig instance void Form1_Click(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Form1::Form1_Click .method private hidebysig instance void Form1_Load(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Form1::Form1_Load .method private hidebysig instance void Form1_Enter(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Form1::Form1_Enter .method private hidebysig instance void Form1_DoubleClick(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 1 (0x1) .maxstack 1 .locals init ([0] class [mscorlib]System.Exception ee) IL_0000: ret } // end of method Form1::Form1_DoubleClick        可以看到Form1_Click、Form1_Load、Form1_Enter中的try catch已经被编译器优化掉了:IL_0000: ret //即为 return 标记 返回值

        只有Form1_DoubleClick中的try catch中对try catch进行了处理:

.locals init ([0] class [mscorlib]System.Exception ee) //定义 Exception 类型参数 ee (此时已经把ee存入了Call Stack中)        即在Form1_DoubleClick中的try catch才会对性能产生影响。

==》可以看出一下三种try catch的写法对于release版本的代码来说是完全一样,也不会产生任何的性能消耗:

try { } catch { throw; } try { } catch (Exception) { throw; } try { } catch (Exception ee) { throw; }对于上面的结论大家可以写测试demo验证一下 (已测试,结果与分析一致)。       

那么对于debug模式下的IL代码是什么样子的呢?

.method private hidebysig instance void Form1_Click(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 11 (0xb) .maxstack 1 IL_0000: nop .try { IL_0001: nop IL_0002: nop IL_0003: leave.s IL_0009 } // end .try catch [mscorlib]System.Object { IL_0005: pop IL_0006: nop IL_0007: rethrow } // end handler IL_0009: nop IL_000a: ret } // end of method Form1::Form1_Click .method private hidebysig instance void Form1_Load(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 11 (0xb) .maxstack 1 IL_0000: nop .try { IL_0001: nop IL_0002: nop IL_0003: leave.s IL_0009 } // end .try catch [mscorlib]System.Exception { IL_0005: pop IL_0006: nop IL_0007: rethrow } // end handler IL_0009: nop IL_000a: ret } // end of method Form1::Form1_Load .method private hidebysig instance void Form1_Enter(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 11 (0xb) .maxstack 1 .locals init ([0] class [mscorlib]System.Exception ee) IL_0000: nop .try { IL_0001: nop IL_0002: nop IL_0003: leave.s IL_0009 } // end .try catch [mscorlib]System.Exception { IL_0005: stloc.0 IL_0006: nop IL_0007: rethrow } // end handler IL_0009: nop IL_000a: ret } // end of method Form1::Form1_Enter .method private hidebysig instance void Form1_DoubleClick(object sender, class [mscorlib]System.EventArgs e) cil managed { // 代码大小 11 (0xb) .maxstack 1 .locals init ([0] class [mscorlib]System.Exception ee) IL_0000: nop .try { IL_0001: nop IL_0002: nop IL_0003: leave.s IL_0009 } // end .try catch [mscorlib]System.Exception { IL_0005: stloc.0 IL_0006: nop IL_0007: ldloc.0 IL_0008: throw } // end handler IL_0009: nop IL_000a: ret } // end of method Form1::Form1_DoubleClick        可以看出四种写法在debug模式下区别只是:rethrow与throw的区别。IL中rethrow与throw分别代表啥呢?

        Throw:引发当前位于计算堆栈上的异常对象。
        Rethrow:再次引发当前异常。

        即当我们抛出一个异常时, CLR会重新设置一个异常起始点。 CLR只记录最近一次异常抛出的位置。下面代码抛出一个异常,从而导致CLR重新设置该异常的起始点:

try { //一些处理 } catch (Exception e) { //一些处理 throw e; //CLR认为这里是异常的起始点 }        相反,如果我们抛出一个异常对象, CLR将不会重新设置其堆栈的起始点,,下面代码抛出一个异常,但不会导致CLR重新设置异常的起始点: try { //一些处理 } catch (Exception e) { //一些处理 throw; //CLR不会重新设置异常的起始点 }        C#中使用throw和throw ex抛出异常,但二者是有区别的。