程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Effective C#原則44:創建應用程序特定的異常類

Effective C#原則44:創建應用程序特定的異常類

編輯:關於C#

異常是一種的報告錯誤的機制,它可以在遠離錯誤發生的地方進行處理錯誤。所有關於錯誤發生的的信息必須包含在異常對象中。在錯誤發生的過程中,你 可能想把底層的錯誤轉化成詳細的應用程序錯誤,而且不丟失關於錯誤的任何信 息。你須要仔細考慮關於如何在C#應用程序中創建特殊的異常類。第一步就是 要理解什麼時候以及為什麼要創建新的異常類,以及如何構造繼承的異常信息。當開發者使用你的庫來寫catch語句時,他們是基於特殊的進行時異常在區別為 同的行為的。每一個不同的異常類可以有不同的處理要完成:

try {
 Foo( );
 Bar( );
} catch( MyFirstApplicationException e1 )
{
 FixProblem( e1 );
} catch( AnotherApplicationException e2 )
{
  ReportErrorAndContinue( e2 );
} catch( YetAnotherApplicationException e3 )
{
  ReportErrorAndShutdown( e3 );
} catch( Exception e )
{
 ReportGenericError( e );
}
finally
{
  CleanupResources( );
}

不同的catch語句可以因為不同的 運行時異常而存在。你,做為庫的作者,當異常的catch語句要處理不同的事情 時,必須創建或者使用不同的異常類。如果不這樣,你的用戶就只有唯一一個無 聊的選擇。在任何一個異常拋出時,你可以掛起或者中止應用程序。這當然是最 少的工作,但樣是不可能從用戶那裡贏得聲望的。或者,他們 可以取得異常, 然後試著斷定這個錯誤是否可以修正:

try {
 Foo( );
 Bar( );
} catch( Exception e )
{
 switch( e.TargetSite.Name )
 {
  case "Foo":
    FixProblem( e );
   break;
  case "Bar":
   ReportErrorAndContinue( e );
   break;
  // some routine called by Foo or Bar:
  default:
    ReportErrorAndShutdown( e );
   break;
 }
} finally
{
 CleanupResources( );
}

這遠不 及使用多個catch語句有吸引力,這是很脆弱的代碼:如果只是常規的修改了名 字,它就被破壞了。如果你移動了造成錯誤的函數調用,放到了一個共享的工具 函數中,它也被破壞了。在更深一層的堆棧上發生異常,就會使這樣的結構變得 更脆弱。

在深入討論這一話題前,讓我先附帶說明兩個不能做承諾的事 情。首先,異常並不能處理你所遇到的所有異常。這並不是一個穩固指導方法, 但我喜歡為錯誤條件拋出異常,這些錯誤條件如果不立即處理或者報告,可能會 在後期產生更嚴重的問題。例如,數據庫裡的數據完整性的錯誤,就應該生產一 個異常。這個問題如果忽略就只會越發嚴重。而像在寫入用戶的窗口位置失敗時 ,不太像是在後來會產生一系列的問題。返回一個錯誤代碼來指示失敗就足夠了 。

其次,寫一個拋出(throw)語句並不意味會在這個時間創建一個新的異 常類。我推薦創建更多的異常,而不是只有少數幾個常規的自然異常:很從人好 像在拋出異常時只對System.Exception情有獨鐘。可惜只只能提供最小的幫助信 息來處理調用代碼。相反,考慮創建一些必須的異常類,可以讓調用代碼明白是 什麼情況,而且提供了最好的機會來恢復它。

再說一遍:實際上要創建 不同的異常類的原則,而且唯一原因是讓你的用戶在寫catch語句來處理錯誤時 更簡單。查看分析這些錯誤條件,看哪些可以放一類裡,成為一個可以恢復錯誤 的行為,然後創建指定的異常類來處理這些行為。你的應用程序可以從一個文件 或者目錄丟失的錯誤中恢復過來嗎?它還可以從安全權限不足的情況下恢復嗎? 網絡資源丟失又會怎樣呢?對於這種遇到不同的錯誤,可能要采取不同的恢復機 制時,你應該為不同的行為創建新的異常類。

因此,現在你應該創建你 自己的異常類了。當你創建一個異常類時,你有很多責任要完成。你應該總是從 System.ApplicationException類派生你的異常類,而不是System.Exception類 。對於這個基類你不用添加太多的功能。對於不同的異常類,它已經具有可以在 不同的catch語句中處理的能力了。

但也不要從異常類中刪除任何東西。 ApplicationException 類有四個不同的構造函數:

// Default constructor
public ApplicationException( );
// Create with a message.
public ApplicationException( string );
// Create with a message and an inner exception.
public ApplicationException( string, Exception );
// Create from an input stream.
protected ApplicationException(
  SerializationInfo, StreamingContext );

當你創建一個新的異 常類時,你應該創建這個四構造函數。不同的情況調用不同的構造方法來構造異 常。你可以委托這個工作給基類來實現:

public class MyAssemblyException :
 ApplicationException
{
 public MyAssemblyException( ) :
  base( )
 {
 }
  public MyAssemblyException( string s ) :
  base( s )
  {
 }
 public MyAssemblyException( string s,
   Exception e) :
  base( s, e )
 {
 }
  protected MyAssemblyException(
  SerializationInfo info, StreamingContext cxt ) :
  base( info, cxt )
 {
 }
}

構造函數須要的這個異常參數值得討論一下。有時候, 你所使用的類庫之一會發生異常。調用你的庫的代碼可能會取得最小的關於可能 修正行為的信息,當你簡單從你使用的異常上傳參數時:

public double DoSomeWork( )
{
 // This might throw an exception defined
 // in the third party library:
 return ThirdPartyLibrary.ImportantRoutine( );
}

當你創建異 常時,你應該提供你自己的庫信息。拋出你自己詳細的異常,以及包含源異常做 為它的內部異常屬性。你可以提供你所能提供的最多的額外信息:

public double DoSomeWork( )
{
 try {
   // This might throw an exception defined
  // in the third party library:
  return ThirdPartyLibrary.ImportantRoutine( );
 } catch( Exception e )
  {
   string msg =
    string.Format("Problem with {0} using library",
     this.ToString( ));
   throw new DoingSomeWorkException( msg, e );
  }
 }
}

這個新的版本會在問題發生的地方創建更多的信息。當你已經創 建了一個恰當的ToString()方法時(參見原則5),你就已經創建了一個可以完整 描述問題發生的異常對象。更多的,一個內聯異常顯示了產生問題的根源:也就 是你所使用的第三方庫裡的一些信息。

這一技術叫做異常轉化,轉化一 個底的層異常到更高級的異常,這樣可以提供更多的關於錯誤的內容。你越是創 建多的關於錯誤的額外的信息,就越是容易讓它用於診斷,以及可能修正錯誤。 通過創建你自己的異常類,你可能轉化底層的問題到詳細的異常,該異常包含所 詳細的應用程序信息,這可以幫助你診斷程序以及盡可能的修正問題。

希望你的應用程序不是經常拋出異常,但它會發生。如果你不做任何詳細的處理 ,你的應用程序可能會產生默認的.Net框架異常,而不管是什麼錯誤在你調用的 方法裡發生。提供更詳細的信息將會讓你以及你的用戶,在實際應用中診斷程序 以及可能的修正錯誤大有幫助。當且僅當對於錯誤有不同的行為要處理時,你才 應該創建不同的異常類。你可以通過提供所有基類支持的構造函數,來創建全功 能的異常類。你還可以使用InnerException屬性來承載底層錯誤條件的所有錯誤 信息。

返回教程目錄

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