談談我處理異常的一般方法

來源:互聯網
上載者:User

http://www.cnblogs.com/loning/archive/2010/10/12/1848860.html

我們在編寫程式的時候會遇到各種各樣的意外情況,如除數為0,數組越界,非法轉型,棧溢出等等。因而我們需要有一種機制來處理這些情況,異常處理就是其中的一種機制。當然,還有其他的機制,在MFC中,由於標準的不統一,就存在著各種錯誤報表方法,如有通過函數返回特殊值的方式,有通過執行某一語句後查詢特殊語句擷取錯誤的碼的方式,等等。

  在C#中,只有一種報告方式,即異常。這樣可以讓開發人員從大量的文檔中解脫出來,不必為一些非邏輯的問題而花費大量時間。

  C#中的異常不同於C++,所有的異常類型都是繼承自System.Exception的,因此我們定義自己的異常類型時,都要繼承自System.Exception或者該類的子類。

  常見的異常語句如下:

 

?
1234567891011 try{  …}catch( InvalidOperationException ex){  ..}catch( Exception ex){  ..  throw
ex;}}finally{  ..}

 

  我們可以通過捕獲不同類型的異常來捕獲我們需要的資訊,而最後一個捕獲Exception的語句將會捕獲未被該語句之前的語句捕獲的資訊。Throw語句可以將捕獲的異常拋出,繼續引發該異常,使得外層的捕獲語句捕獲到該異常,當然,最原始的異常也是通過該語句引發的。

  以上都是一些基本的異常知識。我希望根據自己的經驗,闡述一下自己開發過程中是如何使用異常的。

 

  首先要解決的問題是何時使用異常。我們知道,引發一個異常,需要一個比較大的開銷(http://www.cnblogs.com/aoaoblogs/archive/2009/12/08/1619827.html)。我認為,對於在處理問題主要邏輯範圍以內的問題,不要使用異常。主要邏輯範圍,指的是在完成一件任務的主要路徑。

  比如我現在要進行一個查詢工作,查詢一下使用者在不在,如果該使用者在,那麼我應該返回true,不在則應該返回false,我在用這個方法的時候,我對他的直接期望是得到一個傳回值。然而,在查詢過程中會發生一些其他錯誤,如資料庫無法串連,資料庫並發串連數過大。這些問題,就應當是異常處理所要面對的問題。又比如我希望上傳一個檔案,我用的時候就直接調用該方法,該語句在設計之初,就應當是直接能上傳的。

 

  還有就是什麼樣的問題需要拋出。

  我認為,在設計過程中,在一些不可恢複的情況下,才有拋出異常的必要。應當在文檔中列出較少的可能拋出的異常。我們繼續以查詢使用者為例子,在查詢資料庫的過程中,我們探索資料庫無法串連,如果該查詢方法對時間要求不是很嚴格,我們應當使用一些策略來使得該方法能完成,如再次嘗試串連。在嘗試串連一定次數之後,我們才需要引發異常。

  還有我認為,我們在設計方法的時候,要捕獲所有的方法內語句產生的異常,並將異常封裝後拋出,而不是直接拋出。並且拋出的異常最好是與邏輯相關的。繼續用查詢使用者做例子,資料庫連結出錯、網路不可用之類的問題,我認為為了方便開發人員使用該類(其實有時候開發人員往往就是自己),我們應當將這些異常都放到一個異常類型中,如EnvironmentException,然後定義一個枚舉,將該枚舉作為EnvironmentException的屬性。我們在捕獲異常的時候,只要捕獲該類型的異常就好了,如果希望瞭解異常細節,我們則可以使用InnerException擷取引起該異常的異常。

 

  下面討論怎樣定義異常的問題。

  在.net中有許多內建的異常,如InvalidOperationException、InvalidCastException等,這些異常都繼承自System.SystemException。我在定義異常的時候,通常直接繼承自System.Exception。我個人認為,一個方法不應該拋出太複雜的異常,而使得開發人員困惑。我拋出一個異常,只是把一個方法執行過程中所存在的問題拋出了。我想,得到一個與這個方法邏輯相關的異常、看到名字就大概知道哪裡出錯的異常,總比一個InvalidCastException這樣的異常好多了。

  比如以下是一段語句。

 

?
12345678910 try{    var user=userManager.GetUser(id);  user.Login();  user.Logout();  var article=articleManager.getArticle(articleId);  article.user=user;  article.save();}catch{  …}

 

 

  如果Login()會拋出一個InvalidOperationException,logout()會拋出一個InvalidOperationException,我們可能不得不進一步分析這些異常,然後才能對這些操作作出適當的應對措施。

  對於一個規模不大的類,我習慣於定義包含一個枚舉類型的類的異常。例如,上面這個例子,我會定義UserOperationException, UserManagerOperationException, ArticleOperationExcetpion,ArticleOperation這些異常,以UserOperationException為例子

 

?
123456789 public
class
UserOperationException:Exception{  //僅列出主要部分,建構函式等就略了..  public
enum UserOperationStatus{get;set;}}public
enum
UserOperationStatus{  Login,  Logout,  Save,}

 

 

  這樣在異常捕獲的時候,我們可以捕獲特定的異常來確定哪一塊出現了問題,也可以捕獲Exception來捕獲所有的異常。

  根據我前面說過的設計原則,所有的異常都是經過封裝的已知異常,而且這些異常是基於我們的商務邏輯的,這樣代碼看起來就會比較清晰了。

  如果Login方法還有一些其他的異常資訊,我一般這樣實現。

 

?
1234567 public
enum
LoginOperationException:UserOperationException{  public
LoginOperationException(string
message,ExceptioninnerException)    :base(message,innerException,UserOperationStatus.Login)  {    ..  }}

 

 

  使用這樣的繼承方式,我們就可以更加靈活的來進行異常處理。

 

  現在討論一下如何進行異常捕獲。

  我認為在寫程式的時候,可以首先考慮程式的邏輯問題,非邏輯問題,即異常捕獲可以不進行處理。繼續以使用者登入狀態例子:

 

?
12345678910111213 public
function
void
Login(
string
username,
string
password){
  //_UserManager是類的一個私人欄位  var user=_UserManager.GetUserByName(username);  if(user.password==password)  {    user.Login();    Console.WriteLine(“Success”);    user.Logout();    Console.WriteLine(“LogoutSuccess”);  }else{    Console.WriteLine(“Invalidpassword”);  }}

 

 

  可能我們第一次考慮的時候是這樣的,這是一個最直觀的邏輯,在編寫代碼的過程中,並沒有過多的來關注各個方法拋出的異常。當我們檢查完畢整個邏輯沒有問題之後,就可以對異常進行捕獲了。

 

?
123456789101112131415161718192021222324252627 public
function
void
Login(
string
username,
string
password){
  try{    //_UserManager是類的一個私人欄位    var user=_UserManager.GetUserByName(username);    if(user.password==password)    {      user.Login();      Console.WriteLine(“Success”);      user.Logout();      Console.WriteLine(“Logout Success”);    }else{    Console.WriteLine(“Invalid password”);    }  }catch(UserManagerOperationException){    Console.WriteLine(“User doesn’t exist”);  }catch(UserLoginOperationException ex){    switch(ex.LoginStatus){      case
LoginStatus.DB:        Console.WriteLine(“DB errors”);        break;      case
LoginStatus.Network:        Console.WriteLine(“Network errors”);        break;  }catch(UserOperationExceptionex){    Console.WriteLine(“Other exception {0}”,ex);  }}

 

 

  當然了,這樣的編碼方式還是要在編碼過程中來關注這些異常。在我所瞭解的範圍內,微軟提供了一個開源的企業庫Microsoft Enterprise Library(http://entlib.codeplex.com/),該庫提供了一個ExceptionHandling Application Block,我們可以用更加靈活方式來處理這些異常,比如全部在設定檔裡面做。

  當然,還有其他的方式來解決異常處理的問題,比如AOP技術。最近一直對AOP比較感興趣,但並沒有相關的實踐,因此也沒有什麼經驗可談。

  如果您完整的看完了我的文章,希望您可以根據您的經驗指出文章中存在的問題。這篇文章只是一個我的經驗總結。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.