C# 異常處理(Catch Throw)IL分析
阿新 • • 發佈:2019-02-01
1、catch throw的幾種形式及效能影響:
對應的IL程式碼(以下程式碼是release版本的IL程式碼):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; } }
可以看到Form1_Click、Form1_Load、Form1_Enter中的try catch已經被編譯器優化掉了:.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
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到的異常已經被處理了,只不過處理過程中又丟擲新的異常,從而找不到真正的錯誤源。
throw e重新丟擲異常,並非轉發原來的異常,而會更改包括StackTrace在內的許多異常內部資訊;對於呼叫連很深情況,效能損耗超出想象。
拓展閱讀: