Exception 類別

定義

表示應用程式執行期間發生的錯誤。

public ref class Exception
public ref class Exception : System::Runtime::Serialization::ISerializable
public ref class Exception : System::Runtime::InteropServices::_Exception, System::Runtime::Serialization::ISerializable
public class Exception
public class Exception : System.Runtime.Serialization.ISerializable
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)]
[System.Serializable]
public class Exception : System.Runtime.Serialization.ISerializable
[System.Serializable]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]
[System.Runtime.InteropServices.ComVisible(true)]
public class Exception : System.Runtime.InteropServices._Exception, System.Runtime.Serialization.ISerializable
[System.Serializable]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]
[System.Runtime.InteropServices.ComVisible(true)]
public class Exception : System.Runtime.Serialization.ISerializable
type Exception = class
type Exception = class
    interface ISerializable
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)>]
[<System.Serializable>]
type Exception = class
    interface ISerializable
[<System.Serializable>]
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type Exception = class
    interface ISerializable
    interface _Exception
[<System.Serializable>]
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type Exception = class
    interface ISerializable
Public Class Exception
Public Class Exception
Implements ISerializable
Public Class Exception
Implements _Exception, ISerializable
繼承
Exception
衍生
屬性
實作

範例

下列範例示範 catch F#) 區塊中定義的 (with 來處理 ArithmeticException 錯誤。 此 catch 區塊也會攔截 DivideByZeroException 錯誤,因為 DivideByZeroException 衍生自 ArithmeticException ,而且沒有 catch 明確針對錯誤定義的 DivideByZeroException 區塊。

using namespace System;
int main()
{
    int x = 0;
    try
    {
        int y = 100 / x;
    }
    catch ( ArithmeticException^ e ) 
    {
        Console::WriteLine( "ArithmeticException Handler: {0}", e );
    }
    catch ( Exception^ e ) 
    {
        Console::WriteLine( "Generic Exception Handler: {0}", e );
    }
}
/*
This code example produces the following results:

ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
   at main()
 
*/
using System;

class ExceptionTestClass
{
   public static void Main()
   {
      int x = 0;
      try
      {
         int y = 100 / x;
      }
      catch (ArithmeticException e)
      {
         Console.WriteLine($"ArithmeticException Handler: {e}");
      }
      catch (Exception e)
      {
         Console.WriteLine($"Generic Exception Handler: {e}");
      }
   }	
}
/*
This code example produces the following results:

ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
   at ExceptionTestClass.Main()

*/
module ExceptionTestModule

open System

let x = 0
try
    let y = 100 / x
    ()
with
| :? ArithmeticException as e ->
    printfn $"ArithmeticException Handler: {e}"
| e ->
    printfn $"Generic Exception Handler: {e}"

// This code example produces the following results:
//     ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
//        at <StartupCode$fs>.$ExceptionTestModule.main@()
Class ExceptionTestClass
   
   Public Shared Sub Main()
      Dim x As Integer = 0
      Try
         Dim y As Integer = 100 / x
      Catch e As ArithmeticException
         Console.WriteLine("ArithmeticException Handler: {0}", e.ToString())
      Catch e As Exception
         Console.WriteLine("Generic Exception Handler: {0}", e.ToString())
      End Try
   End Sub
End Class
'
'This code example produces the following results:
'
'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow.
'   at ExceptionTestClass.Main()
'

備註

此類別是所有例外狀況的基類。 發生錯誤時,系統或目前正在執行的應用程式會擲回包含錯誤相關資訊的例外狀況來報告錯誤。 擲回例外狀況之後,會由應用程式或預設例外狀況處理常式處理。

本節內容:

錯誤和例外狀況
Try/catch 區塊
例外狀況類型功能
例外狀況類別屬性
效能考慮
重新擲回例外狀況
選擇標準例外狀況
實作自訂例外狀況

錯誤和例外狀況

執行時間錯誤可能會因為各種原因而發生。 不過,並非所有錯誤都應該處理為程式碼中的例外狀況。 以下是一些可在執行時間發生的錯誤類別,以及回應錯誤的適當方式。

  • 使用錯誤。 使用錯誤代表程式邏輯中可能導致例外狀況的錯誤。 不過,錯誤應該不會透過例外狀況處理來解決,而是透過修改錯誤碼來處理。 例如,下列範例中 方法的 Object.Equals(Object) 覆寫假設 obj 引數必須一律為非 Null。

    using System;
    
    public class Person
    {
       private string _name;
    
       public string Name
       {
          get { return _name; }
          set { _name = value; }
       }
    
       public override int GetHashCode()
       {
          return this.Name.GetHashCode();
       }
    
       public override bool Equals(object obj)
       {
          // This implementation contains an error in program logic:
          // It assumes that the obj argument is not null.
          Person p = (Person) obj;
          return this.Name.Equals(p.Name);
       }
    }
    
    public class Example
    {
       public static void Main()
       {
          Person p1 = new Person();
          p1.Name = "John";
          Person p2 = null;
    
          // The following throws a NullReferenceException.
          Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
       }
    }
    
    // In F#, null is not a valid state for declared types 
    // without 'AllowNullLiteralAttribute'
    [<AllowNullLiteral>]
    type Person() =
        member val Name = "" with get, set
    
        override this.GetHashCode() =
            this.Name.GetHashCode()
    
        override this.Equals(obj) =
            // This implementation contains an error in program logic:
            // It assumes that the obj argument is not null.
            let p = obj :?> Person
            this.Name.Equals p.Name
    
    let p1 = Person()
    p1.Name <- "John"
    let p2: Person = null
    
    // The following throws a NullReferenceException.
    printfn $"p1 = p2: {p1.Equals p2}"
    
    Public Class Person
       Private _name As String
       
       Public Property Name As String
          Get
             Return _name
          End Get
          Set
             _name = value
          End Set
       End Property
       
       Public Overrides Function Equals(obj As Object) As Boolean
          ' This implementation contains an error in program logic:
          ' It assumes that the obj argument is not null.
          Dim p As Person = CType(obj, Person)
          Return Me.Name.Equals(p.Name)
       End Function
    End Class
    
    Module Example
       Public Sub Main()
          Dim p1 As New Person()
          p1.Name = "John"
          Dim p2 As Person = Nothing
          
          ' The following throws a NullReferenceException.
          Console.WriteLine("p1 = p2: {0}", p1.Equals(p2))   
       End Sub
    End Module
    

    NullReferenceException藉由修改原始程式碼以在呼叫 Object.Equals 覆寫並重新編譯之前明確測試 null,以排除 時 obj null 所產生的例外狀況。 下列範例包含處理引數的更正原始程式碼 null

    using System;
    
    public class Person
    {
       private string _name;
    
       public string Name
       {
          get { return _name; }
          set { _name = value; }
       }
    
       public override int GetHashCode()
       {
          return this.Name.GetHashCode();
       }
    
       public override bool Equals(object obj)
       {
           // This implementation handles a null obj argument.
           Person p = obj as Person;
           if (p == null)
              return false;
           else
              return this.Name.Equals(p.Name);
       }
    }
    
    public class Example
    {
       public static void Main()
       {
          Person p1 = new Person();
          p1.Name = "John";
          Person p2 = null;
    
          Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
       }
    }
    // The example displays the following output:
    //        p1 = p2: False
    
    // In F#, null is not a valid state for declared types 
    // without 'AllowNullLiteralAttribute'
    [<AllowNullLiteral>]
    type Person() =
        member val Name = "" with get, set
    
        override this.GetHashCode() =
            this.Name.GetHashCode()
    
        override this.Equals(obj) =
            // This implementation handles a null obj argument.
            match obj with
            | :? Person as p -> 
                this.Name.Equals p.Name
            | _ ->
                false
    
    let p1 = Person()
    p1.Name <- "John"
    let p2: Person = null
    
    printfn $"p1 = p2: {p1.Equals p2}"
    // The example displays the following output:
    //        p1 = p2: False
    
    Public Class Person
       Private _name As String
       
       Public Property Name As String
          Get
             Return _name
          End Get
          Set
             _name = value
          End Set
       End Property
       
       Public Overrides Function Equals(obj As Object) As Boolean
          ' This implementation handles a null obj argument.
          Dim p As Person = TryCast(obj, Person)
          If p Is Nothing Then 
             Return False
          Else
             Return Me.Name.Equals(p.Name)
          End If
       End Function
    End Class
    
    Module Example
       Public Sub Main()
          Dim p1 As New Person()
          p1.Name = "John"
          Dim p2 As Person = Nothing
          
          Console.WriteLine("p1 = p2: {0}", p1.Equals(p2))   
       End Sub
    End Module
    ' The example displays the following output:
    '       p1 = p2: False
    

    您可以使用 Debug.Assert 方法來識別偵錯組建中的使用錯誤,以及 Trace.Assert 識別偵錯和發行組建中的使用錯誤的方法,而不是使用例外狀況處理。 如需詳細資訊,請參閱受控碼中的判斷提示

  • 程式錯誤。 程式錯誤是執行時間錯誤,無法透過撰寫無 Bug 程式碼來避免。

    在某些情況下,程式錯誤可能會反映預期或常式錯誤狀況。 在此情況下,您可能想要避免使用例外狀況處理來處理常式錯誤,並改為重試作業。 例如,如果使用者預期要以特定格式輸入日期,您可以呼叫 DateTime.TryParseExact 方法來剖析日期字串,這個方法會傳回值,指出剖析作業是否成功,而不是使用 DateTime.ParseExact 方法,如果日期字串無法轉換成 DateTime 值,則會擲 FormatExceptionBoolean 例外狀況。 同樣地,如果使用者嘗試開啟不存在的檔案,您可以先呼叫 File.Exists 方法來檢查檔案是否存在,如果不存在,則提示使用者是否要建立檔案。

    在其他情況下,程式錯誤會反映可在程式碼中處理的非預期錯誤狀況。 例如,即使您已檢查以確定檔案存在,它可能先刪除,才能開啟檔案,或可能已損毀。 在此情況下,嘗試藉由具 StreamReader 現化 物件或呼叫 Open 方法來開啟檔案可能會擲回 FileNotFoundException 例外狀況。 在這些情況下,您應該使用例外狀況處理從錯誤中復原。

  • 系統失敗。 系統失敗是執行階段錯誤,無法以有意義的方式以程式設計方式處理。 例如,如果 Common Language Runtime 無法配置額外的記憶體,任何方法都可以擲回 OutOfMemoryException 例外狀況。 一般而言,系統失敗不會使用例外狀況處理來處理。 相反地,您可以使用這類 AppDomain.UnhandledException 事件,並呼叫 Environment.FailFast 方法來記錄例外狀況資訊,並在應用程式終止之前通知使用者失敗。

Try/catch 區塊

Common Language Runtime 提供例外狀況處理模型,此模型是以例外狀況表示為物件,並將程式碼和例外狀況處理常式代碼分隔成 try 區塊和 catch 區塊。 可以有一或多個 catch 區塊,每個區塊都設計成處理特定類型的例外狀況,或一個區塊設計來攔截比另一個區塊更特定的例外狀況。

如果應用程式處理在應用程式程式碼區塊執行期間發生的例外狀況,程式碼必須放在 語句中 try ,而且稱為 try 區塊。 處理區塊擲回 try 之例外狀況的應用程式程式碼會放在 語句中 catch ,並稱為 catch 區塊。 零或多個 catch 區塊會與 try 區塊相關聯,而且每個 catch 區塊都包含一個類型篩選準則,以決定其處理的例外狀況類型。

在區塊中 try 發生例外狀況時,系統會依其出現在應用程式程式碼中的順序搜尋相關聯的 catch 區塊,直到找到 catch 處理例外狀況的區塊為止。 catch如果 catch 區塊的類型篩選指定 T 或衍生自的任何類型 T ,區塊會處理 類型的 T 例外狀況。 系統會在找到處理例外狀況的第一個 catch 區塊之後停止搜尋。 因此,在應用程式程式碼中, catch 必須在處理其基底類型的區塊之前 catch 指定處理類型的區塊,如本節後面的範例所示。 最後指定控制碼 System.Exception 的 catch 區塊。

如果目前區塊沒有 catch 與目前 try 區塊相關聯的區塊會處理例外狀況,而且目前的 try 區塊會巢狀于目前呼叫中的其他 try 區塊內, catch 則會搜尋與下一個封入 try 區塊相關聯的區塊。 如果找不到 catch 例外狀況的區塊,系統會在目前的呼叫中搜尋先前的巢狀層級。 如果在目前的呼叫中找不到 catch 例外狀況的區塊,則會將例外狀況傳遞至呼叫堆疊,而先前的堆疊框架則會 catch 搜尋處理例外狀況的區塊。 呼叫堆疊的搜尋會繼續,直到處理例外狀況,或直到呼叫堆疊上沒有任何框架存在為止。 如果到達呼叫堆疊的頂端,但找不到 catch 處理例外狀況的區塊,預設例外狀況處理常式就會處理它,而應用程式會終止。

F# try..with Expression

F# 不使用 catch 區塊。 相反地,使用單 with 一區塊比對引發的例外狀況。 由於這是運算式,而不是 語句,因此所有路徑都必須傳回相同的類型。 若要深入瞭解,請參閱 試用...搭配 Expression

例外狀況類型功能

例外狀況類型支援下列功能:

  • 描述錯誤的人類可讀文字。 發生例外狀況時,執行時間會提供文字訊息,通知使用者錯誤的性質,並建議解決問題的動作。 這個文字訊息會保留在 Message 例外狀況物件的 屬性中。 在建立例外狀況物件期間,您可以將文字字串傳遞至建構函式,以描述該特定例外狀況的詳細資料。 如果未將錯誤訊息引數提供給建構函式,則會使用預設錯誤訊息。 如需詳細資訊,請參閱 Message 屬性 (Property)。

  • 擲回例外狀況時呼叫堆疊的狀態。 StackTrace屬性具有堆疊追蹤,可用來判斷錯誤在程式碼中發生的位置。 堆疊追蹤會列出呼叫所在來源檔案中所有已呼叫的方法和行號。

例外狀況類別屬性

類別 Exception 包含許多屬性,可協助識別程式碼位置、類型、說明檔,以及例外狀況的原因: StackTraceInnerException 、、 MessageSource TargetSite HelpLink HResult 和 。 Data

當兩個或多個例外狀況之間存在因果關係時, InnerException 屬性會維護這項資訊。 為了回應這個內部例外狀況,會擲回外部例外狀況。 處理外部例外狀況的程式碼可以使用先前內部例外狀況中的資訊,更適當地處理錯誤。 例外狀況的補充資訊可以儲存為 屬性中的 Data 索引鍵/值組集合。

在建立例外狀況物件期間傳遞至建構函式的錯誤訊息字串應該已當地語系化,而且可以使用 類別從資源檔 ResourceManager 提供。 如需當地語系化資源的詳細資訊,請參閱 建立附屬元件封裝和部署資源 主題。

若要為使用者提供有關例外狀況發生原因的詳細資訊, HelpLink 屬性可以保存 URL (或 URN) 至說明檔。

類別 Exception 會使用 HRESULT COR_E_EXCEPTION,其值為 0x80131500。

如需 類別實例 Exception 的初始屬性值清單,請參閱建 Exception 構函式。

效能考量

擲回或處理例外狀況會耗用大量的系統資源和執行時間。 只擲回例外狀況來處理真正異常的情況,而不是處理可預測的事件或流程式控制制。 例如,在某些情況下,例如當您開發類別庫時,如果方法引數無效,就很合理地擲回例外狀況,因為您預期使用有效的參數呼叫方法。 不正確方法引數,如果不是使用錯誤的結果,表示發生異常狀況。 相反地,如果使用者輸入無效,請勿擲回例外狀況,因為您可以預期使用者偶爾輸入不正確資料。 請改為提供重試機制,讓使用者可以輸入有效的輸入。 您也不應該使用例外狀況來處理使用錯誤。 請改用 判斷提示 來識別並更正使用錯誤。

此外,當傳回碼足夠時,請勿擲回例外狀況;請勿將傳回碼轉換為例外狀況;和 不會定期攔截例外狀況、忽略它,然後繼續處理。

重新擲回例外狀況

在許多情況下,例外狀況處理常式只會想要將例外狀況傳遞給呼叫端。 這最常發生在:

  • 類別庫,接著會將呼叫包裝.NET Framework類別庫或其他類別庫中的方法。

  • 遇到嚴重例外狀況的應用程式或程式庫。 例外狀況處理常式可以記錄例外狀況,然後重新擲回例外狀況。

重新擲回例外狀況的建議方式是在 C# 中使用throw語句、F# 中的reraise函式,以及 Visual Basic 中的Throw語句,而不包含運算式。 這可確保當例外狀況傳播至呼叫端時,會保留所有呼叫堆疊資訊。 下列範例將說明這點。 字串擴充方法 FindOccurrences 會包裝一或多個 呼叫, String.IndexOf(String, Int32) 而不需事先驗證其引數。

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

public static class Library
{
   public static int[] FindOccurrences(this String s, String f)
   {
      var indexes = new List<int>();
      int currentIndex = 0;
      try {
         while (currentIndex >= 0 && currentIndex < s.Length) {
            currentIndex = s.IndexOf(f, currentIndex);
            if (currentIndex >= 0) {
               indexes.Add(currentIndex);
               currentIndex++;
            }
         }
      }
      catch (ArgumentNullException e) {
         // Perform some action here, such as logging this exception.

         throw;
      }
      return indexes.ToArray();
   }
}
open System

module Library = 
    let findOccurrences (s: string) (f: string) =
        let indexes = ResizeArray()
        let mutable currentIndex = 0
        try
            while currentIndex >= 0 && currentIndex < s.Length do
                currentIndex <- s.IndexOf(f, currentIndex)
                if currentIndex >= 0 then
                    indexes.Add currentIndex
                    currentIndex <- currentIndex + 1
        with :? ArgumentNullException ->
            // Perform some action here, such as logging this exception.
            reraise ()
        indexes.ToArray()
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Module Library
   <Extension()>
   Public Function FindOccurrences(s As String, f As String) As Integer()
      Dim indexes As New List(Of Integer)
      Dim currentIndex As Integer = 0
      Try
         Do While currentIndex >= 0 And currentIndex < s.Length
            currentIndex = s.IndexOf(f, currentIndex)
            If currentIndex >= 0 Then
               indexes.Add(currentIndex)
               currentIndex += 1
            End If
         Loop
      Catch e As ArgumentNullException
         ' Perform some action here, such as logging this exception.
         
         Throw
      End Try
      Return indexes.ToArray()
   End Function
End Module

接著呼叫端會呼叫 FindOccurrences 兩次。 第二次呼叫 時,呼叫 FindOccurrences 端會傳遞 null 做為搜尋字串的 ,這會導致 String.IndexOf(String, Int32) 方法擲回 ArgumentNullException 例外狀況。 這個例外狀況是由 FindOccurrences 方法處理,並傳回給呼叫端。 因為 throw 語句與沒有運算式搭配使用,所以範例的輸出會顯示呼叫堆疊會保留下來。

public class Example
{
   public static void Main()
   {
      String s = "It was a cold day when...";
      int[] indexes = s.FindOccurrences("a");
      ShowOccurrences(s, "a", indexes);
      Console.WriteLine();

      String toFind = null;
      try {
         indexes = s.FindOccurrences(toFind);
         ShowOccurrences(s, toFind, indexes);
      }
      catch (ArgumentNullException e) {
         Console.WriteLine("An exception ({0}) occurred.",
                           e.GetType().Name);
         Console.WriteLine("Message:\n   {0}\n", e.Message);
         Console.WriteLine("Stack Trace:\n   {0}\n", e.StackTrace);
      }
   }

   private static void ShowOccurrences(String s, String toFind, int[] indexes)
   {
      Console.Write("'{0}' occurs at the following character positions: ",
                    toFind);
      for (int ctr = 0; ctr < indexes.Length; ctr++)
         Console.Write("{0}{1}", indexes[ctr],
                       ctr == indexes.Length - 1 ? "" : ", ");

      Console.WriteLine();
   }
}
// The example displays the following output:
//    'a' occurs at the following character positions: 4, 7, 15
//
//    An exception (ArgumentNullException) occurred.
//    Message:
//       Value cannot be null.
//    Parameter name: value
//
//    Stack Trace:
//          at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
//    ngComparison comparisonType)
//       at Library.FindOccurrences(String s, String f)
//       at Example.Main()
open Library

let showOccurrences toFind (indexes: int[]) =
    printf $"'{toFind}' occurs at the following character positions: "
    for i = 0 to indexes.Length - 1 do
        printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}"""
    printfn ""

let s = "It was a cold day when..."
let indexes = findOccurrences s "a"
showOccurrences "a" indexes
printfn ""

let toFind: string = null
try
    let indexes = findOccurrences s toFind
    showOccurrences toFind indexes

with :? ArgumentNullException as e ->
    printfn $"An exception ({e.GetType().Name}) occurred."
    printfn $"Message:\n   {e.Message}\n"
    printfn $"Stack Trace:\n   {e.StackTrace}\n"

// The example displays the following output:
//    'a' occurs at the following character positions: 4, 7, 15
//
//    An exception (ArgumentNullException) occurred.
//    Message:
//       Value cannot be null. (Parameter 'value')
//
//    Stack Trace:
//          at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
//    ngComparison comparisonType)
//       at Library.findOccurrences(String s, String f)
//       at <StartupCode$fs>.main@()
Module Example
   Public Sub Main()
      Dim s As String = "It was a cold day when..."
      Dim indexes() As Integer = s.FindOccurrences("a")
      ShowOccurrences(s, "a", indexes)
      Console.WriteLine()

      Dim toFind As String = Nothing
      Try
         indexes = s.FindOccurrences(toFind)
         ShowOccurrences(s, toFind, indexes)
      Catch e As ArgumentNullException
         Console.WriteLine("An exception ({0}) occurred.",
                           e.GetType().Name)
         Console.WriteLine("Message:{0}   {1}{0}", vbCrLf, e.Message)
         Console.WriteLine("Stack Trace:{0}   {1}{0}", vbCrLf, e.StackTrace)
      End Try
   End Sub
   
   Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer())
      Console.Write("'{0}' occurs at the following character positions: ",
                    toFind)
      For ctr As Integer = 0 To indexes.Length - 1
         Console.Write("{0}{1}", indexes(ctr),
                       If(ctr = indexes.Length - 1, "", ", "))
      Next
      Console.WriteLine()
   End Sub
End Module
' The example displays the following output:
'    'a' occurs at the following character positions: 4, 7, 15
'
'    An exception (ArgumentNullException) occurred.
'    Message:
'       Value cannot be null.
'    Parameter name: value
'
'    Stack Trace:
'          at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
'    ngComparison comparisonType)
'       at Library.FindOccurrences(String s, String f)
'       at Example.Main()

相反地,如果使用 重新擲回例外狀況

throw e;
Throw e  
raise e

語句不會保留完整的呼叫堆疊,而且此範例會產生下列輸出:

'a' occurs at the following character positions: 4, 7, 15  

An exception (ArgumentNullException) occurred.  
Message:  
   Value cannot be null.  
Parameter name: value  

Stack Trace:  
      at Library.FindOccurrences(String s, String f)  
   at Example.Main()  

較麻煩的替代方法是擲回新的例外狀況,並在內部例外狀況中保留原始例外狀況的呼叫堆疊資訊。 接著,呼叫端可以使用新例外狀況的 InnerException 屬性來擷取堆疊框架,以及原始例外狀況的其他資訊。 在此情況下,throw 語句為:

throw new ArgumentNullException("You must supply a search string.",
                                e);
raise (ArgumentNullException("You must supply a search string.", e) )
Throw New ArgumentNullException("You must supply a search string.",
                                e)

處理例外狀況的使用者程式碼必須知道 InnerException 屬性包含原始例外狀況的相關資訊,如下列例外狀況處理常式所示。

try {
   indexes = s.FindOccurrences(toFind);
   ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e) {
   Console.WriteLine("An exception ({0}) occurred.",
                     e.GetType().Name);
   Console.WriteLine("   Message:\n{0}", e.Message);
   Console.WriteLine("   Stack Trace:\n   {0}", e.StackTrace);
   Exception ie = e.InnerException;
   if (ie != null) {
      Console.WriteLine("   The Inner Exception:");
      Console.WriteLine("      Exception Name: {0}", ie.GetType().Name);
      Console.WriteLine("      Message: {0}\n", ie.Message);
      Console.WriteLine("      Stack Trace:\n   {0}\n", ie.StackTrace);
   }
}
// The example displays the following output:
//    'a' occurs at the following character positions: 4, 7, 15
//
//    An exception (ArgumentNullException) occurred.
//       Message: You must supply a search string.
//
//       Stack Trace:
//          at Library.FindOccurrences(String s, String f)
//       at Example.Main()
//
//       The Inner Exception:
//          Exception Name: ArgumentNullException
//          Message: Value cannot be null.
//    Parameter name: value
//
//          Stack Trace:
//          at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
//    ngComparison comparisonType)
//       at Library.FindOccurrences(String s, String f)
try
    let indexes = findOccurrences s toFind
    showOccurrences toFind indexes
with :? ArgumentNullException as e ->
    printfn $"An exception ({e.GetType().Name}) occurred."
    printfn $"   Message:\n{e.Message}"
    printfn $"   Stack Trace:\n   {e.StackTrace}"
    let ie = e.InnerException
    if ie <> null then
        printfn "   The Inner Exception:"
        printfn $"      Exception Name: {ie.GetType().Name}"
        printfn $"      Message: {ie.Message}\n"
        printfn $"      Stack Trace:\n   {ie.StackTrace}\n"
// The example displays the following output:
//    'a' occurs at the following character positions: 4, 7, 15
//
//    An exception (ArgumentNullException) occurred.
//       Message: You must supply a search string.
//
//       Stack Trace:
//          at Library.FindOccurrences(String s, String f)
//       at Example.Main()
//
//       The Inner Exception:
//          Exception Name: ArgumentNullException
//          Message: Value cannot be null.
//    Parameter name: value
//
//          Stack Trace:
//          at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
//    ngComparison comparisonType)
//       at Library.FindOccurrences(String s, String f)
Try
   indexes = s.FindOccurrences(toFind)
   ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
   Console.WriteLine("An exception ({0}) occurred.",
                     e.GetType().Name)
   Console.WriteLine("   Message: {1}{0}", vbCrLf, e.Message)
   Console.WriteLine("   Stack Trace:{0}   {1}{0}", vbCrLf, e.StackTrace)
   Dim ie As Exception = e.InnerException
   If ie IsNot Nothing Then
      Console.WriteLine("   The Inner Exception:")
      Console.WriteLine("      Exception Name: {0}", ie.GetType().Name)
      Console.WriteLine("      Message: {1}{0}", vbCrLf, ie.Message)
      Console.WriteLine("      Stack Trace:{0}   {1}{0}", vbCrLf, ie.StackTrace)
   End If
End Try
' The example displays the following output:
'       'a' occurs at the following character positions: 4, 7, 15
'
'       An exception (ArgumentNullException) occurred.
'          Message: You must supply a search string.
'
'          Stack Trace:
'             at Library.FindOccurrences(String s, String f)
'          at Example.Main()
'
'          The Inner Exception:
'             Exception Name: ArgumentNullException
'             Message: Value cannot be null.
'       Parameter name: value
'
'             Stack Trace:
'             at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
'       ngComparison comparisonType)
'          at Library.FindOccurrences(String s, String f)

選擇標準例外狀況

當您必須擲回例外狀況時,通常可以使用.NET Framework中的現有例外狀況類型,而不是實作自訂例外狀況。 您應該在下列兩個情況下使用標準例外狀況類型:

  • 您擲回的例外狀況是由使用錯誤 (所造成,也就是呼叫方法) 開發人員的程式邏輯發生錯誤。 一般而言,您會擲回例外狀況,例如 ArgumentExceptionArgumentNullExceptionInvalidOperationExceptionNotSupportedException 。 您在具現化例外狀況物件時提供給例外狀況物件的建構函式的字串應該描述錯誤,讓開發人員可以修正它。 如需詳細資訊,請參閱 Message 屬性 (Property)。

  • 您正在處理可與呼叫端通訊且具有現有.NET Framework例外狀況的錯誤。 您應該擲回可能最衍生的例外狀況。 例如,如果方法需要引數為列舉型別的有效成員,您應該擲 InvalidEnumArgumentException 回 (衍生類別) ,而不是 ArgumentException

下表列出常見的例外狀況類型和擲回這些類型的條件。

例外狀況 條件
ArgumentException 傳遞至方法的非 Null 引數無效。
ArgumentNullException 傳遞至方法的引數為 null
ArgumentOutOfRangeException 引數超出有效值的範圍。
DirectoryNotFoundException 目錄路徑的一部分無效。
DivideByZeroException 整數或 Decimal 除法運算中的分母為零。
DriveNotFoundException 磁片磁碟機無法使用或不存在。
FileNotFoundException 檔案不存在。
FormatException 值不是適當的格式,無法透過轉換方法從字串轉換,例如 Parse
IndexOutOfRangeException 索引超出陣列或集合的界限。
InvalidOperationException 方法呼叫在物件的目前狀態中無效。
KeyNotFoundException 找不到存取集合中成員的指定索引鍵。
NotImplementedException 未實作方法或作業。
NotSupportedException 不支援方法或作業。
ObjectDisposedException 作業會在已處置的物件上執行。
OverflowException 算術、轉型或轉換運算會導致溢位。
PathTooLongException 路徑或檔案名超過系統定義的長度上限。
PlatformNotSupportedException 目前平臺不支援此作業。
RankException 維度數目錯誤的陣列會傳遞至 方法。
TimeoutException 配置給作業的時間間隔已過期。
UriFormatException 使用不正確統一資源識別項 (URI) 。

實作自訂例外狀況

在下列情況下,使用現有的.NET Framework例外狀況來處理錯誤狀況並不夠:

  • 當例外狀況反映無法對應至現有.NET Framework例外狀況的唯一程式錯誤時。

  • 當例外狀況需要處理與適用于現有.NET Framework例外狀況的處理不同時,或例外狀況必須與類似的例外狀況厘清。 例如,如果您在剖析超出目標整數型別範圍的字串數值表示時擲回 ArgumentOutOfRangeException 例外狀況,您就不想針對呼叫端未在呼叫方法時提供適當限制值的錯誤使用相同的例外狀況。

類別 Exception 是.NET Framework中所有例外狀況的基類。 許多衍生類別都依賴 類別成員 Exception 的繼承行為;它們不會覆寫 的成員 Exception ,也不會定義任何唯一的成員。

若要定義您自己的例外狀況類別:

  1. 定義繼承自 Exception 的類別。 如有必要,請定義類別所需的任何唯一成員,以提供例外狀況的其他資訊。 例如,類別 ArgumentException 包含屬性 ParamName ,指定引數造成例外狀況的參數名稱,而 RegexMatchTimeoutException 屬性包含 MatchTimeout 指出逾時間隔的屬性。

  2. 如有必要,請覆寫您想要變更或修改其功能的任何繼承成員。 請注意,大部分的現有衍生類別 Exception 不會覆寫繼承成員的行為。

  3. 判斷您的自訂例外狀況物件是否可序列化。 序列化可讓您儲存例外狀況的相關資訊,並允許遠端內容中的伺服器和用戶端 Proxy 共用例外狀況資訊。 若要讓例外狀況物件可序列化,請使用 屬性標記它 SerializableAttribute

  4. 定義例外狀況類別的建構函式。 一般而言,例外狀況類別具有下列一或多個建構函式:

下列範例說明如何使用自訂例外狀況類別。 它會藉由指定不是質數的起始編號,定義 NotPrimeException 當用戶端嘗試擷取質數序列時擲回的例外狀況。 例外狀況會定義新的屬性 ,這個屬性 NonPrime 會傳回造成例外狀況的非質數。 除了實作受保護的無參數建構函式,以及具有 SerializationInfoStreamingContext 參數進行序列化的建構函式,類別 NotPrimeException 還定義三個額外的建構函式來支援 NonPrime 屬性。 除了保留非質數的值之外,每個建構函式還會呼叫基類建構函式。 類別 NotPrimeException 也會以 SerializableAttribute 屬性標示。

using System;
using System.Runtime.Serialization;

[Serializable()]
public class NotPrimeException : Exception
{
   private int notAPrime;

   protected NotPrimeException()
      : base()
   { }

   public NotPrimeException(int value) :
      base(String.Format("{0} is not a prime number.", value))
   {
      notAPrime = value;
   }

   public NotPrimeException(int value, string message)
      : base(message)
   {
      notAPrime = value;
   }

   public NotPrimeException(int value, string message, Exception innerException) :
      base(message, innerException)
   {
      notAPrime = value;
   }

   protected NotPrimeException(SerializationInfo info,
                               StreamingContext context)
      : base(info, context)
   { }

   public int NonPrime
   { get { return notAPrime; } }
}
namespace global

open System
open System.Runtime.Serialization

[<Serializable>]
type NotPrimeException = 
    inherit Exception
    val notAPrime: int

    member this.NonPrime =
        this.notAPrime

    new (value) =
        { inherit Exception($"%i{value} is not a prime number."); notAPrime = value }

    new (value, message) =
        { inherit Exception(message); notAPrime = value }

    new (value, message, innerException: Exception) =
        { inherit Exception(message, innerException); notAPrime = value }

    // F# does not support protected members
    new () = 
        { inherit Exception(); notAPrime = 0 }

    new (info: SerializationInfo, context: StreamingContext) =
        { inherit Exception(info, context); notAPrime = 0 }
Imports System.Runtime.Serialization

<Serializable()> _
Public Class NotPrimeException : Inherits Exception
   Private notAPrime As Integer

   Protected Sub New()
      MyBase.New()
   End Sub

   Public Sub New(value As Integer)
      MyBase.New(String.Format("{0} is not a prime number.", value))
      notAPrime = value
   End Sub

   Public Sub New(value As Integer, message As String)
      MyBase.New(message)
      notAPrime = value
   End Sub

   Public Sub New(value As Integer, message As String, innerException As Exception)
      MyBase.New(message, innerException)
      notAPrime = value
   End Sub

   Protected Sub New(info As SerializationInfo,
                     context As StreamingContext)
      MyBase.New(info, context)
   End Sub

   Public ReadOnly Property NonPrime As Integer
      Get
         Return notAPrime
      End Get
   End Property
End Class

PrimeNumberGenerator下列範例所示的 類別會使用 Eratosthenes 的 Sieve,將質數序列從 2 計算為用戶端在其類別建構函式呼叫中指定的限制。 方法 GetPrimesFrom 會傳回大於或等於指定下限的所有質數,但如果該下限不是質數,則會擲回 NotPrimeException

using System;
using System.Collections.Generic;

[Serializable]
public class PrimeNumberGenerator
{
   private const int START = 2;
   private int maxUpperBound = 10000000;
   private int upperBound;
   private bool[] primeTable;
   private List<int> primes = new List<int>();

   public PrimeNumberGenerator(int upperBound)
   {
      if (upperBound > maxUpperBound)
      {
         string message = String.Format(
                           "{0} exceeds the maximum upper bound of {1}.",
                           upperBound, maxUpperBound);
         throw new ArgumentOutOfRangeException(message);
      }
      this.upperBound = upperBound;
      // Create array and mark 0, 1 as not prime (True).
      primeTable = new bool[upperBound + 1];
      primeTable[0] = true;
      primeTable[1] = true;

      // Use Sieve of Eratosthenes to determine prime numbers.
      for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
            ctr++)
      {
         if (primeTable[ctr]) continue;

         for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++)
            if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true;
      }
      // Populate array with prime number information.
      int index = START;
      while (index != -1)
      {
         index = Array.FindIndex(primeTable, index, (flag) => !flag);
         if (index >= 1)
         {
            primes.Add(index);
            index++;
         }
      }
   }

   public int[] GetAllPrimes()
   {
      return primes.ToArray();
   }

   public int[] GetPrimesFrom(int prime)
   {
      int start = primes.FindIndex((value) => value == prime);
      if (start < 0)
         throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime));
      else
         return primes.FindAll((value) => value >= prime).ToArray();
   }
}
namespace global

open System

[<Serializable>]
type PrimeNumberGenerator(upperBound) =
    let start = 2
    let maxUpperBound = 10000000
    let primes = ResizeArray()
    let primeTable = 
        upperBound + 1
        |> Array.zeroCreate<bool>

    do
        if upperBound > maxUpperBound then
            let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}."
            raise (ArgumentOutOfRangeException message)
        
        // Create array and mark 0, 1 as not prime (True).
        primeTable[0] <- true
        primeTable[1] <- true

        // Use Sieve of Eratosthenes to determine prime numbers.
        for i = start to float upperBound |> sqrt |> ceil |> int do
            if not primeTable[i] then
                for multiplier = i to upperBound / i do
                    if i * multiplier <= upperBound then
                        primeTable[i * multiplier] <- true
        
        // Populate array with prime number information.
        let mutable index = start
        while index <> -1 do
            index <- Array.FindIndex(primeTable, index, fun flag -> not flag)
            if index >= 1 then
                primes.Add index
                index <- index + 1

    member _.GetAllPrimes() =
        primes.ToArray()

    member _.GetPrimesFrom(prime) =
        let start = 
            Seq.findIndex ((=) prime) primes
        
        if start < 0 then
            raise (NotPrimeException(prime, $"{prime} is not a prime number.") )
        else
            Seq.filter ((>=) prime) primes
            |> Seq.toArray
Imports System.Collections.Generic

<Serializable()> Public Class PrimeNumberGenerator
   Private Const START As Integer = 2
   Private maxUpperBound As Integer = 10000000
   Private upperBound As Integer
   Private primeTable() As Boolean
   Private primes As New List(Of Integer)

   Public Sub New(upperBound As Integer)
      If upperBound > maxUpperBound Then
         Dim message As String = String.Format(
             "{0} exceeds the maximum upper bound of {1}.",
             upperBound, maxUpperBound)
         Throw New ArgumentOutOfRangeException(message)
      End If
      Me.upperBound = upperBound
      ' Create array and mark 0, 1 as not prime (True).
      ReDim primeTable(upperBound)
      primeTable(0) = True
      primeTable(1) = True

      ' Use Sieve of Eratosthenes to determine prime numbers.
      For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound)))
         If primeTable(ctr) Then Continue For

         For multiplier As Integer = ctr To CInt(upperBound \ ctr)
            If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True
         Next
      Next
      ' Populate array with prime number information.
      Dim index As Integer = START
      Do While index <> -1
         index = Array.FindIndex(primeTable, index, Function(flag)
                                                       Return Not flag
                                                    End Function)
         If index >= 1 Then
            primes.Add(index)
            index += 1
         End If
      Loop
   End Sub

   Public Function GetAllPrimes() As Integer()
      Return primes.ToArray()
   End Function

   Public Function GetPrimesFrom(prime As Integer) As Integer()
      Dim start As Integer = primes.FindIndex(Function(value)
                                                 Return value = prime
                                              End Function)
      If start < 0 Then
         Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime))
      Else
         Return primes.FindAll(Function(value)
                                  Return value >= prime
                               End Function).ToArray()
      End If
   End Function
End Class

下列範例會使用非質數對 方法進行兩次呼叫 GetPrimesFrom ,其中一個會跨越應用程式域界限。 在這兩種情況下,都會擲回例外狀況,並在用戶端程式代碼中成功處理。

using System;
using System.Reflection;

class Example
{
   public static void Main()
   {
      int limit = 10000000;
      PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
      int start = 1000001;
      try
      {
         int[] values = primes.GetPrimesFrom(start);
         Console.WriteLine("There are {0} prime numbers from {1} to {2}",
                           start, limit);
      }
      catch (NotPrimeException e)
      {
         Console.WriteLine("{0} is not prime", e.NonPrime);
         Console.WriteLine(e);
         Console.WriteLine("--------");
      }

      AppDomain domain = AppDomain.CreateDomain("Domain2");
      PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
                                        typeof(Example).Assembly.FullName,
                                        "PrimeNumberGenerator", true,
                                        BindingFlags.Default, null,
                                        new object[] { 1000000 }, null, null);
      try
      {
         start = 100;
         Console.WriteLine(gen.GetPrimesFrom(start));
      }
      catch (NotPrimeException e)
      {
         Console.WriteLine("{0} is not prime", e.NonPrime);
         Console.WriteLine(e);
         Console.WriteLine("--------");
      }
   }
}
open System
open System.Reflection

let limit = 10000000
let primes = PrimeNumberGenerator limit
let start = 1000001
try
    let values = primes.GetPrimesFrom start
    printfn $"There are {values.Length} prime numbers from {start} to {limit}"
with :? NotPrimeException as e ->
    printfn $"{e.NonPrime} is not prime"
    printfn $"{e}"
    printfn "--------"

let domain = AppDomain.CreateDomain "Domain2"
let gen = 
    domain.CreateInstanceAndUnwrap(
        typeof<PrimeNumberGenerator>.Assembly.FullName,
        "PrimeNumberGenerator", true,
        BindingFlags.Default, null,
        [| box 1000000 |], null, null)
    :?> PrimeNumberGenerator
try
    let start = 100
    printfn $"{gen.GetPrimesFrom start}"
with :? NotPrimeException as e ->
    printfn $"{e.NonPrime} is not prime"
    printfn $"{e}"
    printfn "--------"
Imports System.Reflection

Module Example
   Sub Main()
      Dim limit As Integer = 10000000
      Dim primes As New PrimeNumberGenerator(limit)
      Dim start As Integer = 1000001
      Try
         Dim values() As Integer = primes.GetPrimesFrom(start)
         Console.WriteLine("There are {0} prime numbers from {1} to {2}",
                           start, limit)
      Catch e As NotPrimeException
         Console.WriteLine("{0} is not prime", e.NonPrime)
         Console.WriteLine(e)
         Console.WriteLine("--------")
      End Try

      Dim domain As AppDomain = AppDomain.CreateDomain("Domain2")
      Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap(
                                        GetType(Example).Assembly.FullName,
                                        "PrimeNumberGenerator", True,
                                        BindingFlags.Default, Nothing,
                                        {1000000}, Nothing, Nothing)
      Try
         start = 100
         Console.WriteLine(gen.GetPrimesFrom(start))
      Catch e As NotPrimeException
         Console.WriteLine("{0} is not prime", e.NonPrime)
         Console.WriteLine(e)
         Console.WriteLine("--------")
      End Try
   End Sub
End Module
' The example displays the following output:
'      1000001 is not prime
'      NotPrimeException: 1000001 is not a prime number.
'         at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
'         at Example.Main()
'      --------
'      100 is not prime
'      NotPrimeException: 100 is not a prime number.
'         at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
'         at Example.Main()
'      --------

Windows 執行階段 和 .NET Framework 4.5.1

在適用于 Windows 8 Windows 8.x Store 應用程式的 .NET 中,某些例外狀況資訊通常會在例外狀況透過非.NET Framework堆疊框架傳播時遺失。 從 .NET Framework 4.5.1 和 Windows 8.1 開始,Common Language Runtime 會繼續使用擲回的原始 Exception 物件,除非在非.NET Framework堆疊框架中修改該例外狀況。

建構函式

Exception()

初始化 Exception 類別的新執行個體。

Exception(SerializationInfo, StreamingContext)

使用序列化資料,初始化 Exception 類別的新執行個體。

Exception(String)

使用指定的錯誤訊息,初始化 Exception 類別的新執行個體。

Exception(String, Exception)

使用指定的錯誤訊息以及造成此例外狀況的內部例外狀況的參考,初始化 Exception 類別的新執行個體。

屬性

Data

取得鍵值組的集合,這些鍵值組會提供關於例外狀況的其他使用者定義資訊。

HelpLink

取得或設定與這個例外狀況相關聯的說明檔連結。

HResult

取得或設定 HRESULT,它是指派給特定例外狀況的編碼數值。

InnerException

取得造成目前例外狀況的 Exception 執行個體。

Message

取得描述目前例外狀況的訊息。

Source

取得或設定造成錯誤的應用程式或物件的名稱。

StackTrace

取得呼叫堆疊上即時運算框架的字串表示。

TargetSite

取得擲回目前例外狀況的方法。

方法

Equals(Object)

判斷指定的物件是否等於目前的物件。

(繼承來源 Object)
GetBaseException()

在衍生類別中覆寫時,傳回一或多個後續的例外狀況的根本原因 Exception

GetHashCode()

做為預設雜湊函式。

(繼承來源 Object)
GetObjectData(SerializationInfo, StreamingContext)

在衍生類別中覆寫時,使用例外狀況的資訊設定 SerializationInfo

GetType()

取得目前執行個體的執行階段類型。

GetType()

取得目前執行個體的 Type

(繼承來源 Object)
MemberwiseClone()

建立目前 Object 的淺層複製。

(繼承來源 Object)
ToString()

建立並傳回目前例外狀況的字串表示。

事件

SerializeObjectState
已過時。

當例外狀況序列化,以建立包含例外狀況相關序列化資料的例外狀況狀態物件時,就會發生此事件。

適用於

另請參閱