關於.NET異常處理的思考(上)

來源:互聯網
上載者:User
在項目開發中,對於系統和代碼的穩定性和容錯性都是有對應的要求。實際開發項目中的代碼與範例代碼的區別,更多的是在代碼的啟動並執行穩定性、容錯性、擴充性的比較。

因為對於實現一個功能來說,實現功能的核心代碼是一樣的,可能只是在寫法上最佳化而已,但是在實現某一個操作上使用的類來說,這一點是絕大多數時候是一樣的。

這樣看來,我們在實際開發的過程中,需要考慮的問題比較多,已經不僅僅局限於某一具體的功能實現,更多的是代碼的穩定性和擴充性考慮。

以上是在實際開發中需要面對的問題,筆者在最近的博文中,也在考慮這個異常到底需要怎麼去寫,以及異常到底需要怎麼去理解,在博文中,也有不少的園友對異常的寫法和處理提出了自己的意見,在這裡我就寫一下自己的一些理解,可能寫的比較淺顯和粗略,但是只當是一個引子,可以引出大佬們來談談自己的實際項目經驗。希望對大家有一個協助,也歡迎大家提出自己的想法和意見,分享自己的知識和見解。

一.DotNET異常的概述

談到異常,我們就需要知道什麼叫做異常,萬事萬物如果我們想去學習,就應該知道我們要學習的東西是什麼,這樣在心裡也好有一個大概的認知。異常是指成員沒有完成它的名稱宣稱可以完成的行動。

在.NET中,構造器、擷取和設定屬性、添加和刪除事件、叫用作業符重載和調用轉換操作符等等都沒有辦法返回錯誤碼,但是在這些構造中又需要報告錯誤,那就必須提供異常處理機制。

在異常的處理中,我們經常使用到的三個塊分別是:try塊;catch塊;finally塊。這三個塊可以一起使用,也可以不寫catch塊使用,異常處理塊可以嵌套使用,具體的方法在下面會介紹到。

在異常的處理機制中,一般有三種選擇:重新拋出相同的異常,向調用棧高一層的代碼通知該異常的發生;拋出一個不同的異常,想調用棧高一層代碼提供更豐富的異常資訊;讓線程從catch塊的底部退出。

有關異常的處理方式,有一些指導性的建議。

1.恰當的使用finally塊

finally塊可以保證不管線程拋出什麼類型的異常都可以被執行,finall塊一般用來做清理那些已經成功啟動的操作,然後再返回調用者或者finally塊之後的代碼。

2.異常捕捉需適當

為什麼要適當的捕捉異常呢?如下代碼,因為我們不能什麼異常都去捕捉,在捕獲異常後,我們需要去處理這些異常,如果我們將所有的異常都捕捉後,但是沒有預見會發生的異常,我們就沒有辦法去處理這些異常。

如果應用程式代碼拋出一個異常,應用程式的另一端則可能預期要捕捉這個異常,因此不能寫成一個”大小通吃“的異常塊,應該允許該異常在調用棧中向上移動,讓應用程式代碼針對性地處理這個異常。

在catch塊中,可以使用System.Exception捕捉異常,但是最好在catch塊末尾重新拋出異常。至於原因在後面會講解到。

try{  var hkml = GetRegistryKey(rootKey);  var subkey = hkml.CreateSubKey(subKey);  if (subkey != null && keyName != string.Empty)  subkey.SetValue(keyName, keyValue, RegistryValueKind.String);}catch (Exception ex){         Log4Helper.Error("建立註冊表錯誤" + ex);         throw new Exception(ex.Message,ex);}

3.從異常中恢複

我們在捕獲異常後,可以針對性的寫一些異常恢複的代碼,可以讓程式繼續運行。在捕獲異常時,需要捕獲具體的異常,充分的掌握在什麼情況下會拋出異常,並知道從捕獲的異常類型派生出了那些類型。除非在catch塊的末尾重新拋出異常,否則不要處理或捕獲System.Exception異常。

4.維持狀態

一般情況下,我們完成一個操作或者一個方法時,需要調用幾個方法組合完成,在執行的過程中會出現前面幾個方法完成,後面的方法發生異常。發生不可恢複的異常時復原部分完成的操作,因為我們需要恢複資訊,所有我們在捕獲異常時,需要捕獲所有的異常資訊。

5.隱藏實現細節來維持契約

有時可能需要捕捉一個異常並重新拋出一個不同的異常,這樣可以維繫方法的契約,拋出的心異常類型地應該是一個具體的異常。看如下代碼:

FileStream fs = null;            try            {                fs = FileStream();                          }            catch (FileNotFoundException e)            {          //拋出一個不同的異常,將異常資訊包含在其中,並將原來的異常設定為內部異常                throw new NameNotFoundException();            }            catch (IOException e)            {                //拋出一個不同的異常,將異常資訊包含在其中,並將原來的異常設定為內部異常             throw new NameNotFoundException();             }             finally             {               if (fs != null)                 {                fs.close();             }             }

以上的代碼只是在說明一種處理方式。應該讓拋出的所有異常都沿著方法的調用棧向上傳遞,而不是把他們”吞噬“了之後拋出一個新的異常。如果一個類型構造器拋出一個異常,而且該異常未在類型構造器方法中捕獲,CLR就會在內部捕獲該異常,並改為拋出一個新的TypeInitialztionException。

二.DotNET異常的常用處理機制

在代碼發生異常後,我們需要去處理這個異常,如果一個異常沒有得到及時的處理,CLR會終止進程。在異常的處理中,我們可以在一個線程捕獲異常,在另一個線程中重新拋出異常。異常拋出時,CLR會在調用棧中向上尋找與拋出的異常類型匹配的catch塊。如果沒有任何catch塊匹配拋出的異常類型,就發生一個未處理異常。CLR檢測到進程中的任何線程有一個位處理異常,都會終止進程。

1.異常處理塊

(1).try塊:包含代碼通常需要執行一些通用的資源清理操作,或者需要從異常中恢複,或者兩者都需要。try塊還可以包含也許會拋出異常的代碼。一個try塊至少有一個關聯的catch塊或finall塊。

(2).catch塊:包含的是響應一個異常需要執行的代碼。catch關鍵字後的圓括弧中的運算式是捕獲類型。捕獲類型從System.Exception或者其衍生類別指定。CLR自上而下搜素一個匹配的catch塊,所以應該教具體的異常放在頂部。一旦CLR找到一個具有匹配捕獲類型的catch塊,就會執行內層所有finally塊中的代碼,”內層finally“是指拋出異常的tey塊開始,到匹配異常的catch塊之間的所有finally塊。

使用System.Exception捕捉異常後,可以採用在catch塊的末尾重新拋出異常,因為如果我們在捕獲Exception異常後,沒有及時的處理或者終止程式,這一異常可能對程式造成很大的安全隱患,Exception類是所有異常的基類,可以捕獲程式中所有的異常,如果出現較大的異常,我們沒有及時的處理,造成的問題是巨大的。

(3).finally塊:包含的代碼是保證會執行的代碼。finally塊的所有代碼執行完畢後,線程退出finally塊,執行緊跟在finally塊之後的語句。如果不存在finally塊,線程將從最後一個catch塊之後的語句開始執行。

備忘:異常塊可以組合和嵌套,對於三個異常塊的範例,在這裡就不做介紹,異常的嵌套可以防止在處理異常的時候再次出現未處理的異常,以上這些就不再贅述。

2.異常處理執行個體


(1).異常處理擴充方法

 /// <summary>        ///  格式化異常訊息        /// </summary>        /// <param name="e">異常對象</param>        /// <param name="isHideStackTrace">是否隱藏異常規模資訊</param>        /// <returns>格式化後的異常資訊字串</returns>        public static string FormatMessage(this Exception e, bool isHideStackTrace = false)        {            var sb = new StringBuilder();            var count = 0;            var appString = string.Empty;            while (e != null)            {                if (count > 0)                {                    appString += "  ";                }                sb.AppendLine(string.Format("{0}異常訊息:{1}", appString, e.Message));                sb.AppendLine(string.Format("{0}異常類型:{1}", appString, e.GetType().FullName));                sb.AppendLine(string.Format("{0}異常方法:{1}", appString, (e.TargetSite == null ? null : e.TargetSite.Name)));                sb.AppendLine(string.Format("{0}異常源:{1}", appString, e.Source));                if (!isHideStackTrace && e.StackTrace != null)                {                    sb.AppendLine(string.Format("{0}異常堆棧:{1}", appString, e.StackTrace));                }                if (e.InnerException != null)                {                    sb.AppendLine(string.Format("{0}內部異常:", appString));                    count++;                }                e = e.InnerException;            }            return sb.ToString();        }

(2).驗證異常

 /// <summary>        /// 檢查字串是空的或空的,並拋出一個異常        /// </summary>        /// <param name="val">值測試</param>        /// <param name="paramName">參數檢查名稱</param>        public static void CheckNullOrEmpty(string val, string paramName)        {            if (string.IsNullOrEmpty(val))                throw new ArgumentNullException(paramName, "Value can't be null or empty");        }        /// <summary>        /// 請檢查參數不是空的或空的,並拋出異常        /// </summary>        /// <param name="param">檢查值</param>        /// <param name="paramName">參數名稱</param>        public static void CheckNullParam(string param, string paramName)        {            if (string.IsNullOrEmpty(param))                throw new ArgumentNullException(paramName, paramName + " can't be neither null nor empty");        }        /// <summary>        /// 檢查參數不是無效,並拋出一個異常        /// </summary>        /// <param name="param">檢查值</param>        /// <param name="paramName">參數名稱</param>        public static void CheckNullParam(object param, string paramName)        {            if (param == null)                throw new ArgumentNullException(paramName, paramName + " can't be null");        }        /// <summary>        /// 請檢查參數1不同於參數2        /// </summary>        /// <param name="param1">值1測試</param>        /// <param name="param1Name">name of value 1</param>        /// <param name="param2">value 2 to test</param>        /// <param name="param2Name">name of vlaue 2</param>        public static void CheckDifferentsParams(object param1, string param1Name, object param2, string param2Name)        {            if (param1 == param2) {                throw new ArgumentException(param1Name + " can't be the same as " + param2Name,                    param1Name + " and " + param2Name);            }        }        /// <summary>        /// 檢查一個整數值是正的(0或更大)        /// </summary>        /// <param name="val">整數測試</param>        public static void PositiveValue(int val)        {            if (val < 0)                throw new ArgumentException("The value must be greater than or equal to 0.");        }

(3).Try-Catch擴充操作

 /// <summary>        ///     對某對象執行指定功能與後續功能,並處理異常情況        /// </summary>        /// <typeparam name="T">物件類型</typeparam>        /// <param name="source">值</param>        /// <param name="action">要對值執行的主功能代碼</param>        /// <param name="failureAction">catch中的功能代碼</param>        /// <param name="successAction">主功能代碼成功後執行的功能代碼</param>        /// <returns>主功能代碼是否順利執行</returns>        public static bool TryCatch<T>(this T source, Action<T> action, Action<Exception> failureAction,            Action<T> successAction) where T : class        {            bool result;            try            {                action(source);                successAction(source);                result = true;            }            catch (Exception obj)            {                failureAction(obj);                result = false;            }            return result;        }        /// <summary>        ///     對某對象執行指定功能,並處理異常情況        /// </summary>        /// <typeparam name="T">物件類型</typeparam>        /// <param name="source">值</param>        /// <param name="action">要對值執行的主功能代碼</param>        /// <param name="failureAction">catch中的功能代碼</param>        /// <returns>主功能代碼是否順利執行</returns>        public static bool TryCatch<T>(this T source, Action<T> action, Action<Exception> failureAction) where T : class        {            return source.TryCatch(action,                failureAction,                obj => { });        }        /// <summary>        ///     對某對象執行指定功能,並處理異常情況與傳回值        /// </summary>        /// <typeparam name="T">物件類型</typeparam>        /// <typeparam name="TResult">傳回值類型</typeparam>        /// <param name="source">值</param>        /// <param name="func">要對值執行的主功能代碼</param>        /// <param name="failureAction">catch中的功能代碼</param>        /// <param name="successAction">主功能代碼成功後執行的功能代碼</param>        /// <returns>功能代碼的傳回值,如果出現異常,則返回物件類型的預設值</returns>        public static TResult TryCatch<T, TResult>(this T source, Func<T, TResult> func, Action<Exception> failureAction,            Action<T> successAction)            where T : class        {            TResult result;            try            {                var u = func(source);                successAction(source);                result = u;            }            catch (Exception obj)            {                failureAction(obj);                result = default(TResult);            }            return result;        }        /// <summary>        ///     對某對象執行指定功能,並處理異常情況與傳回值        /// </summary>        /// <typeparam name="T">物件類型</typeparam>        /// <typeparam name="TResult">傳回值類型</typeparam>        /// <param name="source">值</param>        /// <param name="func">要對值執行的主功能代碼</param>        /// <param name="failureAction">catch中的功能代碼</param>        /// <returns>功能代碼的傳回值,如果出現異常,則返回物件類型的預設值</returns>        public static TResult TryCatch<T, TResult>(this T source, Func<T, TResult> func, Action<Exception> failureAction)            where T : class        {            return source.TryCatch(func,                failureAction,                obj => { });        }

本文沒有具體介紹try,catch,finally的使用,而是給出一些比較通用的方法,主要是一般的開發人員對於三個塊的使用都有一個認識,就不再做重複的介紹。

以上就是關於.NET異常處理的思考(上)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.