1. 程式人生 > >.net try catch 異常捕獲的正確使用姿勢。。

.net try catch 異常捕獲的正確使用姿勢。。

很慚愧,寫了好多年的程式碼, 最基本的try catch 才剛剛會正確的使用,
以前只能說叫會用, 但是用法不正確。
先說說,異常的3種使用方式。 見下面的程式碼。

        public static int Method1()
        {
            try
            {
                int a=int.Prease("aaaa");
                return Method1_1();
            }
            catch (Exception ex)
            {
                throw
; //方式1, 會丟失本try 程式碼段內的StackTrace資訊 throw ex; //方式2 會丟失本try 程式碼段內的StackTrace資訊,包括子函式內的 throw new Exception(ex); //方式3 新的異常。 會丟失更多。 } }

StackTrace資訊 非常重要,能夠幫助我們快速定位到異常所在的程式碼行,幫我們快速的診斷問題程式碼。 在生產環境中的時候,一般是無法直接除錯的。 那麼捕獲異常並記錄異常所在的行,(前提是要提供除錯庫 pdb檔案,生成的時候回一起自動生成。一起復制過去就可以)能夠幫助我們非常快速的定位異常做出判斷。。

要想能夠正確捕獲異常資訊中的StackTrace資訊,根據上面的程式碼。3中捕獲異常並再次丟擲的方式都會丟失一部分StackTrace 資訊。丟失最小的要數 方式1, 但是,還是丟失了,try 程式碼塊內的StackTrace 資訊, 不信你可以自己測試一下。

那麼這裡就有問題了。 前後邏輯似乎是衝突的嘛。
1.不能除錯。
2.不能丟失StackTrace資訊
3.捕獲了資訊要寫入日誌
4.有些程式碼必須在出現異常的時候執行處理程式碼,然後還得再丟擲異常。例如資料庫事物,在出錯的時候要把事物回滾。

當然,我們有方法處理這種問題。 那就是在每一層,寫一個日誌。
例如

   public
static int Method1() { try { int a=int.Prease("aaaa"); return Method1_1(); } catch (Exception ex) { log.Error(ex); //這裡立即寫入日誌。不會丟失StackTrace資訊 throw ex; //然後直接丟擲,後處理。 } }

當然這麼幹,不是不可以。 但是實際上這樣幹最大的麻煩是,log物件在每一層都需要, 在每個物件裡面都需要。 log成了標配。。 log日誌成了頭疼的問題。

我記得以前我曾經寫過關於異常資訊的捕獲和處理。 我的建議是
在最外層進行捕獲,並寫入日誌。 中間層和底層不要捕獲。
我至今也建議這樣寫。
這樣寫,最大的好處是省事。而且也照樣能控制異常。

那麼問題來了。 中間層, 必須要處理的時候怎麼弄呢? 例如涉及到資料庫關閉, 檔案關閉等。 必須要在出異常關閉資源的時候, 程式碼怎麼寫呢?
中間層,寫了try catch ,不管用方式1,方式2,方式3,都會丟失 StackTrace資訊。
不寫又不行。。。

中間層的最終建議。


 public static int DBSave()
        {
            DbContext DB =  DBContextHelper.GetLISDbContext();   
            var tran = DB.Database.BeginTransaction();
            var isCommit = false;
            try
            {
                。。。。中間業務邏輯程式碼
                tran.Commit();
                isCommit = true;
                return true;
            }
             //catch (Exception ex) 不要捕獲異常,直接外層捕獲,
            finally
            {
                if (isCommit == false)
                {   //如果未能成功提交,也就是中間發生過異常,那麼回滾.這樣捕獲的好處是, 異常資訊的StackStace 不會丟,能快速定位到問題程式碼行
                    tran.Rollback(); 
                } 
                tran.Dispose();
                DB.Dispose();
            }

        }

最外層,的程式碼

try{
    ...呼叫中間層程式碼。
}
 catch (LisException hisex)
{ //呼叫對方系統異常.具體資訊放在Message中
    res.Code = "600";
    res.Message = hisex.Message;
    res.ExceptionDetails = hisex.ToString();
    MyLog(CallMethodName, ParameterXml, string.Empty, res);
}
catch (Exception ex)
{   // 系統異常.具體資訊放在Message中 
    while (ex.InnerException != null) ex = ex.InnerException; //底層的異常容易排除錯誤
    res.Code = "500";
    res.Message = ex.Message;
    res.ExceptionDetails = ex.ToString();
    //return ex.ToString();
    MyLog(CallMethodName, ParameterXml, string.Empty,  res);
}

對於最頂層, 一般是每個按鈕都有一個
try catch
最頂層,配好log就可以和方便的進行日誌記錄了。不必在每一層都寫try catch