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拋出異常,但二者是有區別的。
在C#中推薦使用throw;來拋出異常;throw ex;會將到現在為止的所有信息清空,認為你catch到的異常已經被處理了,只不過處理過程中又拋出新的異常,從而找不到真正的錯誤源。
拓展閱讀:
IL指令詳細
.NET中異常處理的最佳實踐(譯)