編寫高質量代碼改善C#程序的157個建議[避免finaly內的無效代碼、避免嵌套異常、避免吃掉異常、注意循環異常處理]

前言  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,讓效率獲得了極大的提高。

 

相關文章
相關標籤/搜索