C#異常處理的方式
c#中所有可以被拋出的異常都是直接或間接繼承自System.Exception類
支援的捕獲異常的語句塊如下:
try … catch
try … catch … finally
try… finally
c#代碼塊中產生異常堆棧資訊的時機不是在throw語句執行的地方,而是在第一次捕獲的地方
以上三種方式中 try ... finally一定不會影響堆棧資訊
可能會影響的地方主要集中在catch塊中
catch子句聲明方式又有以下幾種
catch{}
catch(Exception){}
catch(Exception ex){}
這三種寫法從捕獲異常的能力上來說基本上是等效
第三種方式只是讓編寫代碼的人可以使用異常參數,如果不使用異常參數的話可以用前面兩種,第二種只是更加明確的指出了捕獲的是Exception或者是從Exception繼承的異常
catch塊內可以再次拋出異常,拋出時可以支援下面幾種方式
throw;
throw ex; // ex來自於catch(Exception ex)
throw new SomeException();
後面兩種情況是一樣的, stack trace認為你catch到的異常已經被處理了,只不過處理過程中又拋出新的異常,這時候stack trace就把throw 後的那個異常執行個體當作錯誤根源了。之前的堆棧資訊全部會清除掉
第一種方式網上和MSDN上都說可以保留堆棧資訊。經測試發現不全是如此:
在Debug版本下,產生的程式碼在執行沒有任何最佳化,直接使用throw;這種方式的確是能保留堆棧資訊。這個時候的堆棧資訊就好像是在try塊內部異常產生的地方就記錄好了一樣。
在Release下,輸出的資訊中只包含了從捕獲位置開始向上的堆棧資訊。即從異常源發生位置到第一次catch塊捕獲發生之間的堆棧資訊都不存在了
總結如下:
以前的異常資訊中有出錯的點很容易就找到了,那是因為正巧異常源與第一次捕獲的地方很接近
否則的話越是在調用的最上層捕獲到異常,越不容易找到異常源在什麼位置(這裡都是在Release條件下)
在Release版本下,異常越早捕獲,越能精確定位出錯位置,越晚捕獲,從堆棧中獲得的獲得的有效資訊也越少,即使是用了throw;這樣的方式也一樣
嚴格禁止 下面這種寫法:
try{
// do
}
catch(Exception ex)
{
throw ex;
}
如有必要需改用 throw; 或者 throw new SomeException(msg, ex); 這樣的形式
如果是捕獲了異常不想再次拋出(一般用在捕獲到的異常在預期範圍內,已經有相應的處理方式了),最好是能記錄日誌,表示發生過異常
附上一個測試:
Release下 使用throw;
Release下 使用throw ex;
Release下更極端的例子去掉了除最外層外所有的try…catch:
Debug下 使用throw;
Debug下 使用throw ex;