雜談try-catch-finally異常處理
相關(guān)閱讀:再談異常處理try catch finally
1. 前言
最近這段時間正開發(fā)一個店鋪管理系統(tǒng),這個項目定位于給中小型店鋪使用的軟件系統(tǒng)。簡單的說,它處理商品的進貨,銷售,退貨等功能。軟件雖小,五臟俱全,里面涉及的技術(shù)跟大型應(yīng)用軟件其實差別也不大,其中有加密、數(shù)據(jù)訪問、異常處理、日志、驗證、ORM、依賴注入等。
本篇文章主要介紹C#語言的異常處理方面的內(nèi)容,其中包含的主要內(nèi)容:
•什么是異常?異常的特點?
•異常處理的基礎(chǔ)知識。
•引發(fā)和捕捉異常的處理準則。
•避免與異常相關(guān)的性能問題的兩種設(shè)計模式。
•微軟企業(yè)庫異常處理模塊。
2. 異常概述
•在應(yīng)用程序遇到異常情況(如被零除情況或內(nèi)存不足警告)時,就會產(chǎn)生異常。
•在可能引發(fā)異常的語句周圍使用 try 塊。
•try 塊中發(fā)生異常后,控制流會立即跳轉(zhuǎn)到關(guān)聯(lián)的異常處理程序(如果存在)。
•如果給定異常沒有異常處理程序,則程序?qū)⑼V箞?zhí)行,并顯示一條錯誤消息。
•如果 catch 塊定義了一個異常變量,則可以使用它來獲取有關(guān)所發(fā)生異常的類型的更多信息。
•可能導(dǎo)致異常的操作通過 try 關(guān)鍵字來執(zhí)行。
•異常處理程序是在異常發(fā)生時執(zhí)行的代碼塊。在 C# 中,catch 關(guān)鍵字用于定義異常處理程序。
•程序可以使用 throw 關(guān)鍵字顯式地引發(fā)異常。
•異常對象包含有關(guān)錯誤的詳細信息,比如調(diào)用堆棧的狀態(tài)以及有關(guān)錯誤的文本說明。
•即使引發(fā)了異常,finally 塊中的代碼也會執(zhí)行,從而使程序可以釋放資源。
3. 異常處理基礎(chǔ)知識
3.1. 如何:使用 Try/Catch 塊捕捉異常
將可能引發(fā)異常的代碼節(jié)放在 Try 塊中,而將處理異常的代碼放在 Catch 塊中。Catch 塊是一系列以關(guān)鍵字 catch 開頭的語句,語句后跟異常類型和要執(zhí)行的操作。
下面的代碼示例使用 Try/Catch 塊捕捉可能的異常。Main 方法包含帶有 StreamReader 語句的 Try 塊,該語句打開名為 data.txt 的數(shù)據(jù)文件并從該文件寫入字符串。Try 塊后面是 Catch 塊,該塊捕捉 Try 塊產(chǎn)生的任何異常。
using System; using System.IO; using System.Security.Permissions; // Security permission request. [assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, All = @"c:\data.txt")] public class ProcessFile { public static void Main() { try { StreamReader sr = File.OpenText("data.txt"); Console.WriteLine("The first line of this file is {0}", sr.ReadLine()); } catch(Exception e) { Console.WriteLine("An error occurred: '{0}'", e); } } }
3.2. 如何:在 Catch 塊中使用特定異常
發(fā)生異常時,異常沿堆棧向上傳遞,每個 Catch 塊都有機會處理它。Catch 語句的順序很重要。將針對特定異常的 Catch 塊放在常規(guī)異常 Catch 塊的前面,否則編譯器可能會發(fā)出錯誤。確定正確 Catch 塊的方法是將異常的類型與 Catch 塊中指定的異常名稱進行匹配。如果沒有特定的 Catch 塊,則由可能存在的常規(guī) Catch 塊捕捉異常。
下面的代碼示例使用 try/catch 塊捕獲 InvalidCastException。該示例創(chuàng)建一個名為 Employee 的類,它帶有一個屬性:職員級別 (Emlevel)。PromoteEmployee 方法取得對象并增加職員級別。將 DateTime 實例傳遞給 PromoteEmployee 方法時,發(fā)生 InvalidCastException。
using System; public class Employee { //Create employee level property. public int Emlevel { get { return(emlevel); } set { emlevel = value; } } int emlevel; } public class Ex13 { public static void PromoteEmployee(Object emp) { //Cast object to Employee. Employee e = (Employee) emp; // Increment employee level. e.Emlevel = e.Emlevel + 1; } public static void Main() { try { Object o = new Employee(); DateTime newyears = new DateTime(2001, 1, 1); //Promote the new employee. PromoteEmployee(o); //Promote DateTime; results in InvalidCastException as newyears is not an employee instance. PromoteEmployee(newyears); } catch (InvalidCastException e) { Console.WriteLine("Error passing data to PromoteEmployee method. " + e); } } }
3.3. 如何:顯式引發(fā)異常
可以使用 throw 語句顯式引發(fā)異常。還可以使用 throw 語句再次引發(fā)捕獲的異常。較好的編碼做法是,向再次引發(fā)的異常添加信息以在調(diào)試時提供更多信息。
下面的代碼示例使用 try/catch 塊捕獲可能的 FileNotFoundException。try 塊后面是 catch 塊,catch 塊捕獲 FileNotFoundException,如果找不到數(shù)據(jù)文件,則向控制臺寫入消息。下一條語句是 throw 語句,該語句引發(fā)新的 FileNotFoundException 并向該異常添加文本信息。
using System; using System.IO; public class ProcessFile { public static void Main() { FileStream fs = null; try { //Opens a text tile. fs = new FileStream(@"C:\temp\data.txt", FileMode.Open); StreamReader sr = new StreamReader(fs); string line; //A value is read from the file and output to the console. line = sr.ReadLine(); Console.WriteLine(line); } catch(FileNotFoundException e) { Console.WriteLine("[Data File Missing] {0}", e); throw new FileNotFoundException(@"data.txt not in c:\temp directory]",e); } finally { if (fs != null) fs.Close(); } } }
3.4. 如何:使用 Finally 塊
異常發(fā)生時,執(zhí)行將終止,并且控制交給最近的異常處理程序。這通常意味著不執(zhí)行希望總是調(diào)用的代碼行。有些資源清理(如關(guān)閉文件)必須總是執(zhí)行,即使有異常發(fā)生。為實現(xiàn)這一點,可以使用 Finally 塊。Finally 塊總是執(zhí)行,不論是否有異常發(fā)生。
下面的代碼示例使用 try/catch 塊捕獲 ArgumentOutOfRangeException。Main 方法創(chuàng)建兩個數(shù)組并試圖將一個數(shù)組復(fù)制到另一個數(shù)組。該操作生成 ArgumentOutOfRangeException,同時錯誤被寫入控制臺。Finally 塊執(zhí)行,不論復(fù)制操作的結(jié)果如何。
using System; class ArgumentOutOfRangeExample { static public void Main() { int[] array1={0,0}; int[] array2={0,0}; try { Array.Copy(array1,array2,-1); } catch (ArgumentOutOfRangeException e) { Console.WriteLine("Error: {0}",e); } finally { Console.WriteLine("This statement is always executed."); } } }
4. 異常設(shè)計準則
4.1. 異常引發(fā)
•不要返回錯誤代碼。異常是報告框架中的錯誤的主要手段。
•盡可能不對正??刂屏魇褂卯惓?。除了系統(tǒng)故障及可能導(dǎo)致爭用狀態(tài)的操作之外,框架設(shè)計人員還應(yīng)設(shè)計一些 API 以便用戶可以編寫不引發(fā)異常的代碼。例如,可以提供一種在調(diào)用成員之前檢查前提條件的方法,以便用戶可以編寫不引發(fā)異常的代碼。
•不要包含可以根據(jù)某一選項引發(fā)或不引發(fā)異常的公共成員。
•不要包含將異常作為返回值或輸出參數(shù)返回的公共成員。
•考慮使用異常生成器方法。從不同的位置引發(fā)同一異常會經(jīng)常發(fā)生。為了避免代碼膨脹,請使用幫助器方法創(chuàng)建異常并初始化其屬性。
•避免從 finally 塊中顯式引發(fā)異常??梢越邮芤蛘{(diào)用引發(fā)異常的方法而隱式引發(fā)的異常。
4.2. 異常處理
•不要通過在框架代碼中捕捉非特定異常(如 System.Exception、System.SystemException 等)來處理錯誤。
•避免通過在應(yīng)用程序代碼中捕捉非特定異常(如 System.Exception、System.SystemException 等)來處理錯誤。某些情況下,可以在應(yīng)用程序中處理錯誤,但這種情況極。
•如果捕捉異常是為了傳輸異常,則不要排除任何特殊異常。
•如果了解特定異常在給定上下文中引發(fā)的條件,請考慮捕捉這些異常。
•不要過多使用 catch。通常應(yīng)允許異常在調(diào)用堆棧中往上傳播。
•使用 try-finally 并避免將 try-catch 用于清理代碼。在書寫規(guī)范的異常代碼中,try-finally 遠比 try-catch 更為常用。
•捕捉并再次引發(fā)異常時,首選使用空引發(fā)。這是保留異常調(diào)用堆棧的最佳方式。
•不要使用無參數(shù) catch 塊來處理不符合 CLS 的異常(不是從 System.Exception 派生的異常)。支持不是從 Exception 派生的異常的語言可以處理這些不符合 CLS 的異常。
5. 兩種設(shè)計模式
5.1. Tester-Doer 模式
Doer 部分
public class Doer { public static void ProcessMessage(string message) { if (message == null) { throw new ArgumentNullException("message"); } } } Tester部分 public class Tester { public static void TesterDoer(ICollection<string> messages) { foreach (string message in messages) { if (message != null) { Doer.ProcessMessage(message); } } } }
5.2. TryParse 模式
TryParse 方法類似于 Parse 方法,不同之處在于 TryParse 方法在轉(zhuǎn)換失敗時不引發(fā)異常。
Parse方法
public void Do() { string s = “a”; double d; try { d = Double.Parse(s); } catch (Exception ex) { d = 0; } }
TryParse方法
public void TryDo() { string s = "a"; double d; if (double.TryParse(s, out d) == false) { d = 0; } }
6. 微軟企業(yè)庫異常處理模塊
6.1. 創(chuàng)建自定義異常包裝類
public class BusinessLayerException : ApplicationException { public BusinessLayerException() : base() { } public BusinessLayerException(string message) : base(message) { } public BusinessLayerException(string message, Exception exception) : base(message, exception) { } protected BusinessLayerException(SerializationInfo info, StreamingContext context) : base(info, context) { } }
6.2. 配置異常處理
<add name="Wrap Policy"> <exceptionTypes> <add type="System.Data.DBConcurrencyException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException" name="DBConcurrencyException"> <exceptionHandlers> <add exceptionMessage="Wrapped Exception: A recoverable error occurred while attempting to access the database." exceptionMessageResourceType="" wrapExceptionType="ExceptionHandlingQuickStart.BusinessLayer.BusinessLayerException, ExceptionHandlingQuickStart.BusinessLayer" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Wrap Handler" /> </exceptionHandlers> </add> </exceptionTypes> </add>
6.3. 編寫代碼
public bool ProcessWithWrap() { try { this.ProcessB(); } catch(Exception ex) { // Quick Start is configured so that the Wrap Policy will // log the exception and then recommend a rethrow. bool rethrow = ExceptionPolicy.HandleException(ex, "Wrap Policy"); if (rethrow) { throw; } } return true; }
小結(jié)
try { //執(zhí)行的代碼,其中可能有異常。一旦發(fā)現(xiàn)異常,則立即跳到catch執(zhí)行。否則不會執(zhí)行catch里面的內(nèi)容 }
catch { //除非try里面執(zhí)行代碼發(fā)生了異常,否則這里的代碼不會執(zhí)行 }
finally { //不管什么情況都會執(zhí)行,包括try catch 里面用了return ,可以理解為只要執(zhí)行了try或者catch,就一定會執(zhí)行 finally }
相關(guān)文章
adonet基礎(chǔ)示例分享(adonet連接數(shù)據(jù)庫)
這篇文章主要介紹了adonet基礎(chǔ)示例分享(adonet連接數(shù)據(jù)庫),需要的朋友可以參考下2014-04-04VsCode使用EmmyLua插件調(diào)試Unity工程Lua代碼的詳細步驟
這篇文章主要介紹了VsCode使用EmmyLua插件調(diào)試Unity工程Lua代碼,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08