Exception 類別
定義
重要
部分資訊涉及發行前產品,在發行之前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。
表示應用程式執行期間發生的錯誤。
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 值,則會擲 FormatException 回 Boolean 例外狀況。 同樣地,如果使用者嘗試開啟不存在的檔案,您可以先呼叫 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.含運算式
F# 不使用 catch
區塊。 相反地,引發的例外狀況是使用單 with
一區塊比對的模式。 由於這是運算式,而不是語句,因此所有路徑都必須傳回相同的類型。 若要深入瞭解,請參閱 試用...含運算式。
例外狀況類型功能
例外狀況類型支援下列功能:
描述錯誤的人類可讀文字。 發生例外狀況時,執行時間會發出簡訊,通知使用者錯誤的性質,並建議解決問題的動作。 這個文字訊息會保留在例外狀況物件的 屬性中 Message 。 在建立例外狀況物件期間,您可以將文字字串傳遞至建構函式,以描述該特定例外狀況的詳細資料。 如果未將錯誤訊息引數提供給建構函式,則會使用預設的錯誤訊息。 如需詳細資訊,請參閱 Message 屬性 (Property)。
擲回例外狀況時呼叫堆疊的狀態。 屬性 StackTrace 具有堆疊追蹤,可用來判斷程式碼中發生錯誤的位置。 堆疊追蹤會列出呼叫所在來源檔案中所有呼叫的方法和行號。
例外狀況類別屬性
類別 Exception 包含許多屬性,可協助識別程式碼位置、類型、說明檔,以及例外狀況的原因: StackTrace 、、 InnerException 、、 HResult HelpLink Source Message TargetSite 和 。 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中的現有例外狀況類型,而不是實作自訂例外狀況。 您應該在下列兩個情況下使用標準例外狀況類型:
您擲回的例外狀況是由使用錯誤 (所造成,也就是呼叫方法) 開發人員的程式邏輯發生錯誤。 一般而言,您會擲回例外狀況,例如 ArgumentException 、 ArgumentNullException 、 InvalidOperationException 或 NotSupportedException 。 您在具現化例外狀況物件時提供給例外狀況物件的建構函式的字串應該描述錯誤,讓開發人員可以修正它。 如需詳細資訊,請參閱 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 ,也不會定義任何唯一的成員。
若要定義您自己的例外狀況類別:
定義繼承自 Exception 的類別。 如有必要,請定義類別所需的任何唯一成員,以提供例外狀況的其他資訊。 例如,類別 ArgumentException 包含屬性 ParamName ,指定引數造成例外狀況的參數名稱,而 RegexMatchTimeoutException 屬性包含 MatchTimeout 指出逾時間隔的屬性。
如有必要,請覆寫您想要變更或修改其功能的任何繼承成員。 請注意,大部分的現有衍生類別 Exception 不會覆寫繼承成員的行為。
判斷您的自訂例外狀況物件是否可序列化。 序列化可讓您儲存例外狀況的相關資訊,並允許遠端內容中的伺服器和用戶端 Proxy 共用例外狀況資訊。 若要讓例外狀況物件可序列化,請使用 屬性標記它 SerializableAttribute 。
定義例外狀況類別的建構函式。 一般而言,例外狀況類別具有下列一或多個建構函式:
Exception(),使用預設值初始化新例外狀況物件的屬性。
Exception(String),它會使用指定的錯誤訊息初始化新的例外狀況物件。
Exception(String, Exception),它會使用指定的錯誤訊息和內部例外狀況,初始化新的例外狀況物件。
Exception(SerializationInfo, StreamingContext),這是
protected
從序列化資料初始化新例外狀況物件的建構函式。 如果您選擇讓例外狀況物件可序列化,您應該實作此建構函式。
下列範例說明如何使用自訂例外狀況類別。 它會藉由指定不是質數的起始編號,定義 NotPrimeException
當用戶端嘗試擷取質數序列時擲回的例外狀況。 例外狀況會定義新的屬性 ,這個屬性 NonPrime
會傳回造成例外狀況的非質數。 除了實作受保護的無參數建構函式,以及具有 SerializationInfo 和 StreamingContext 參數進行序列化的建構函式,類別 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 Microsoft 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 |
已過時。
當例外狀況序列化,以建立包含例外狀況相關序列化資料的例外狀況狀態物件時,就會發生此事件。 |