程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 編寫高質量代碼改善C#程序的157個建議[避免finaly內的無效代碼、避免嵌套異常、避免吃掉異常、注意循環異常處理]

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

編輯:C#入門知識

前言  

  本文已同步到http://www.cnblogs.com/aehyok/p/3624579.html。本文主要來學習以下幾點建議

  建議61、避免在finally內撰寫無效代碼

  建議62、避免嵌套異常

  建議63、避免“吃掉”異常

  建議64、為循環增加Tester-Doer模式而不是將try-catch置於循環內

建議61、避免在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執行完畢。");
            }
        }

看完代碼你心裡大概也有了一個答案了吧

這些如果通過IL來解釋,還是比較容易的,在此就不進行贅述了。

  在CLR中,方法的參數以及返回值都是用棧來保存的。在方法內部,會首先將參數依次壓棧,當需要使用這些參數的時候,方法會直接去棧裡取用參數值,方法返回時,會將返回值壓入棧頂。如果參數的類型是值類型,壓棧的就是復制的值,如果是引用類型,則在方法內對於參數的修改也會帶到方法外。

建議62、避免嵌套異常  

在建議59中已經強調過,應該允許異常在調用堆棧中往上傳播,不要過多使用catch,然後再throw。果斷使用catch會帶來兩個問題:

1、代碼更多了。這看上去好像你根本不知道該怎麼處理異常,所以你總在不停地catch.

2、隱藏了堆棧信息,使你不知道真正發生異常的地方。

來看一下下面的代碼

        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將會重置堆棧消息。

 

建議63、避免“吃掉”異常 

  看了建議62之後,你可能已經明白,嵌套異常是很危險的行為,一不小心就會將異常堆棧信息,也就是真正的Bug出處隱藏起來。但這還不是最嚴重的行為,最嚴重的就是“吃掉”異常,即捕獲然後不向上層throw拋出。

  避免“吃掉”異常,並不是說不應該“吃掉”異常,而是這裡面有個重要原則:該異常可悲預見,並且通常情況它不能算是一個Bug。

  想象你正在對上萬份文件進行解密,這些文件來自不同的客戶端,很有可能存在文件倍破壞的現象,你的目標就是要講解密出來的數據插入數據庫。這個時候,你不得不忽略那些解密失敗的問題,讓這個過程進行下去。當然,記錄日志是必要的, 因為後期你可能會倍解密失敗的文件做統一的處理。

  另外一種情況,可能連記錄日志都不需要。在對上千個受控端進行控制的分布式系統中,控制端需要發送心跳數據來判斷受控端的在線情況。通常的做法是維護一個信號量,如果在一個可接受的阻滯時間如(如500ms)心跳數據發送失敗,那麼控制端線程將不會收到信號,即可以判斷受控端的斷線狀態。在這種情況下,對每次SocketException進行記錄,通常也是沒有意義的。

  本建議的全部要素是:如果你不知道如何處理某個異常,那麼千萬不要“吃掉”異常,如果你一不小“吃掉”了一個本該網上傳遞的異常,那麼,這裡可能誕生一個BUg,而且,解決它會很費周折。 

建議64、為循環增加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,讓效率得到了極大的提升。

 

英語小貼士

1、 How much?—— 多少錢?
2、I'm full.—— 我飽了。
3、 I'm home.—— 我回來了。
4、 I'm lost. ——我迷路了。
5、 My treat.—— 我請客。
6、 So do I.—— 我也一樣。
7、 This way.—— 這邊請。
8、 After you.—— 您先。
9、 Bless you! ——祝福你!
10、 Follow me.—— 跟我來。

作者:aehyok

出處:http://www.cnblogs.com/aehyok/

感謝您的閱讀,如果您對我的博客所講述的內容有興趣,那不妨點個推薦吧,謝謝支持:-O。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved