前言 html
本文已同步到http://www.cnblogs.com/aehyok/p/3624579.html。本文主要來學習如下幾點建議數據庫
建議6一、避免在finally內撰寫無效代碼less
建議6二、避免嵌套異常分佈式
建議6三、避免「吃掉」異常ide
建議6四、爲循環增長Tester-Doer模式而不是將try-catch置於循環內性能
建議6一、避免在finally內撰寫無效代碼學習
先直接來看一下三個簡單的try catch方法測試
public class User { public string Name { get; set; } } class Program { static void Main(string[] args) { Console.WriteLine(Test1()); Console.WriteLine(Test2()); Console.WriteLine(Test3().Name); Console.ReadLine(); } public static int Test1() { int i = 0; try { i = 1; } finally { i = 2; Console.WriteLine("\t 將int結果改成2,finnally執行完畢。"); } return i; } public static int Test2() { int i = 0; try { return i = 1; } finally { i = 2; Console.WriteLine("\t 將int結果改成2,finnally執行完畢。"); } }
看完代碼你內心大概也有了一個答案了吧spa
這些若是經過IL來解釋,仍是比較容易的,在此就不進行贅述了。線程
在CLR中,方法的參數以及返回值都是用棧來保存的。在方法內部,會首先將參數依次壓棧,當須要使用這些參數的時候,方法會直接去棧裏取用參數值,方法返回時,會將返回值壓入棧頂。若是參數的類型是值類型,壓棧的就是複製的值,若是是引用類型,則在方法內對於參數的修改也會帶到方法外。
建議6二、避免嵌套異常
在建議59中已經強調過,應該容許異常在調用堆棧中往上傳播,不要過多使用catch,而後再throw。果斷使用catch會帶來兩個問題:
一、代碼更多了。這看上去好像你根本不知道該怎麼處理異常,因此你總在不停地catch.
二、隱藏了堆棧信息,使你不知道真正發生異常的地方。
來看一下下面的代碼
static void Main(string[] args) { try { Console.WriteLine("NoTry\n"); MethodNoTry(); } catch (Exception exception) { Console.WriteLine(exception.StackTrace); } try { Console.WriteLine("WithTry\n"); MethodWithTry(); } catch (Exception exception) { Console.WriteLine(exception.StackTrace); } Console.ReadLine(); } public static int Method() { int i = 0; return 10 / i; } public static void MethodNoTry() { Method(); } public static void MethodWithTry() { try { Method(); } catch (Exception exception) { throw exception; } }
執行結果
能夠發現,MethodNoTry的方法能夠查看到發生異常錯誤的地方,而MethodWithTry根本不清楚發生錯誤的地方了。調用的堆棧倍重置了。若是這個方法還存在另外的異常,在UI層將永遠不知道真正發生錯誤的地方,給開發者帶來不小的麻煩。
除了在建議59中提到的須要包裝異常的狀況外,無端地嵌套異常是咱們要極力避免的。固然,若是真得須要捕獲這個異常來恢復一些狀態,而後從新拋出,代碼來起來應該能夠這樣:
try { MethodWithTry(); } catch(Exception) { ///工做代碼 throw; }
或者稍做改動
try { MethodWithTry(); } catch { ///工做代碼 throw; }
儘可能避免下面這樣引起異常:
try { MethodWithTry(); } catch(Exception exception) { ///工做代碼 throw exception; }
直接throw exception而不是throw將會重置堆棧消息。
建議6三、避免「吃掉」異常
看了建議62以後,你可能已經明白,嵌套異常是很危險的行爲,一不當心就會將異常堆棧信息,也就是真正的Bug出處隱藏起來。但這還不是最嚴重的行爲,最嚴重的就是「吃掉」異常,即捕獲而後不向上層throw拋出。
避免「吃掉」異常,並非說不該該「吃掉」異常,而是這裏面有個重要原則:該異常可悲預見,而且一般狀況它不能算是一個Bug。
想象你正在對上萬份文件進行解密,這些文件來自不一樣的客戶端,頗有可能存在文件倍破壞的現象,你的目標就是要講解密出來的數據插入數據庫。這個時候,你不得不忽略那些解密失敗的問題,讓這個過程進行下去。固然,記錄日誌是必要的, 由於後期你可能會倍解密失敗的文件作統一的處理。
另一種狀況,可能連記錄日誌都不須要。在對上千個受控端進行控制的分佈式系統中,控制端須要發送心跳數據來判斷受控端的在線狀況。一般的作法是維護一個信號量,若是在一個可接受的阻滯時間如(如500ms)心跳數據發送失敗,那麼控制端線程將不會收到信號,便可以判斷受控端的斷線狀態。在這種狀況下,對每次SocketException進行記錄,一般也是沒有意義的。
本建議的所有要素是:若是你不知道如何處理某個異常,那麼千萬不要「吃掉」異常,若是你一不小「吃掉」了一個本該網上傳遞的異常,那麼,這裏可能誕生一個BUg,並且,解決它會很費周折。
建議6四、爲循環增長Tester-Doer模式而不是將try-catch置於循環內
若是須要在循環中引起異常,你須要特別注意,由於拋出異常是一個至關影響性能的過程。應該儘可能在循環當中對異常發生的一些條件進行判斷,而後根據條件進行處理。能夠作一個測試:
static void Main(string[] args) { CodeTimer.Initialize(); CodeTimer.Time("try..catch..", 1, P1); CodeTimer.Time("Tester-Doer", 1, P2); Console.ReadLine(); } public static void P1() { int x = 0; for (int i = 0; i < 1000; i++) { try { int j = i / x; } catch { } } }
差距至關明顯。以上代碼中,咱們預見了代碼可能會發生DivideByZeroException異常,因而,調整策略,對異常發生的條件進行了特殊處理:Continue,讓效率獲得了極大的提高。