System.Exception, klasa
Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.
Klasa Exception jest klasą bazową dla wszystkich wyjątków. W przypadku wystąpienia błędu system lub aktualnie wykonująca aplikację zgłasza go, zgłaszając wyjątek zawierający informacje o błędzie. Po wystąpieniu wyjątku jest on obsługiwany przez aplikację lub przez domyślną procedurę obsługi wyjątków.
Błędy i wyjątki
Błędy czasu wykonywania mogą wystąpić z różnych powodów. Jednak nie wszystkie błędy powinny być obsługiwane jako wyjątki w kodzie. Poniżej przedstawiono niektóre kategorie błędów, które mogą wystąpić w czasie wykonywania i odpowiednie sposoby reagowania na nie.
Błędy użycia. Błąd użycia reprezentuje błąd logiki programu, który może spowodować wyjątek. Jednak błąd powinien zostać rozwiązany nie za pomocą obsługi wyjątków, ale przez zmodyfikowanie wadliwego kodu. Na przykład zastąpienie Object.Equals(Object) metody w poniższym przykładzie zakłada, że
obj
argument musi być zawsze inny niż null.using System; public class Person1 { 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. Person1 p = (Person1) obj; return this.Name.Equals(p.Name); } } public class UsageErrorsEx1 { public static void Main() { Person1 p1 = new Person1(); p1.Name = "John"; Person1 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 Example2 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
Wyjątek NullReferenceException , który powoduje, że wyniki, gdy
obj
null
można wyeliminować, modyfikując kod źródłowy w celu jawnego przetestowania wartości null przed wywołaniem Object.Equals zastąpienia, a następnie ponownie skompilując. Poniższy przykład zawiera poprawiony kod źródłowy, który obsługujenull
argument.using System; public class Person2 { 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. Person2 p = obj as Person2; if (p == null) return false; else return this.Name.Equals(p.Name); } } public class UsageErrorsEx2 { public static void Main() { Person2 p1 = new Person2(); p1.Name = "John"; Person2 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 Person2 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 Person2 = TryCast(obj, Person2) If p Is Nothing Then Return False Else Return Me.Name.Equals(p.Name) End If End Function End Class Module Example3 Public Sub Main() Dim p1 As New Person2() p1.Name = "John" Dim p2 As Person2 = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: False
Zamiast używać obsługi wyjątków dla błędów użycia, można użyć Debug.Assert metody do identyfikowania błędów użycia w kompilacjach debugowania oraz Trace.Assert metody identyfikowania błędów użycia zarówno w kompilacjach debugowania, jak i kompilacji wydania. Aby uzyskać więcej informacji, zobacz Asercji w kodzie zarządzanym.
Błędy programu. Błąd programu jest błędem czasu wykonywania, którego nie można uniknąć przez napisanie kodu bez usterek.
W niektórych przypadkach błąd programu może odzwierciedlać oczekiwany lub rutynowy stan błędu. W takim przypadku możesz unikać używania obsługi wyjątków w celu obsługi błędów programu i zamiast tego ponowić próbę wykonania operacji. Jeśli na przykład użytkownik ma wprowadzić datę w określonym formacie, możesz przeanalizować ciąg daty, wywołując DateTime.TryParseExact metodę , która zwraca wartość wskazującą Boolean , czy operacja analizowania zakończyła się pomyślnie, zamiast używać DateTime.ParseExact metody , która zgłasza FormatException wyjątek, jeśli nie można przekonwertować ciągu daty na DateTime wartość. Podobnie, jeśli użytkownik próbuje otworzyć plik, który nie istnieje, możesz najpierw wywołać File.Exists metodę , aby sprawdzić, czy plik istnieje, a jeśli nie, monituj użytkownika, czy chce go utworzyć.
W innych przypadkach błąd programu odzwierciedla nieoczekiwany warunek błędu, który można obsłużyć w kodzie. Na przykład, nawet jeśli sprawdzono, czy plik istnieje, może zostać usunięty, zanim będzie można go otworzyć lub może być uszkodzony. W takim przypadku próba otwarcia pliku przez utworzenie wystąpienia obiektu lub wywołanie StreamReaderOpen metody może zgłosić FileNotFoundException wyjątek. W takich przypadkach należy użyć obsługi wyjątków, aby odzyskać sprawę po błędzie.
Błędy systemowe. Awaria systemu to błąd czasu wykonywania, który nie może być obsługiwany programowo w zrozumiały sposób. Na przykład każda metoda może zgłosić OutOfMemoryException wyjątek, jeśli środowisko uruchomieniowe języka wspólnego nie może przydzielić dodatkowej pamięci. Zazwyczaj awarie systemu nie są obsługiwane przy użyciu obsługi wyjątków. Zamiast tego może być możliwe użycie zdarzenia, takiego jak AppDomain.UnhandledException i wywołanie Environment.FailFast metody w celu rejestrowania informacji o wyjątkach i powiadamiania użytkownika o niepowodzeniu przed zakończeniem działania aplikacji.
Bloki try/catch
Środowisko uruchomieniowe języka wspólnego udostępnia model obsługi wyjątków oparty na reprezentacji wyjątków jako obiektów oraz rozdzielenie kodu programu i obsługi wyjątków w try
blokach i catch
blokach. Może istnieć jeden lub więcej catch
bloków, z których każda została zaprojektowana do obsługi określonego typu wyjątku, lub jeden blok zaprojektowany w celu przechwycenia bardziej szczegółowego wyjątku niż inny blok.
Jeśli aplikacja obsługuje wyjątki występujące podczas wykonywania bloku kodu aplikacji, kod musi zostać umieszczony w try
instrukcji i jest nazywany blokiem try
. Kod aplikacji, który obsługuje wyjątki zgłaszane przez try
blok, jest umieszczany w catch
instrukcji i jest nazywany blokiem catch
. Z blokiem try
jest skojarzonych zero lub więcej catch
bloków, a każdy catch
blok zawiera filtr typu, który określa typy obsługiwanych wyjątków.
W przypadku wystąpienia wyjątku w try
bloku system przeszukuje skojarzone catch
bloki w kolejności, w której są wyświetlane w kodzie aplikacji, dopóki nie znajdzie bloku obsługującego catch
wyjątek. Blok catch
obsługuje wyjątek typu T
, jeśli filtr typu bloku catch określa T
lub dowolny typ, z którego T
pochodzi. System zatrzymuje wyszukiwanie po znalezieniu pierwszego catch
bloku obsługującego wyjątek. Z tego powodu w kodzie aplikacji należy określić blok obsługujący catch
typ przed blokiem catch
obsługującym jego typy podstawowe, jak pokazano w przykładzie, który jest zgodny z tą sekcją. Ostatnio określono blok catch, który obsługuje System.Exception
.
Jeśli żaden z bloków catch
skojarzonych z bieżącym try
blokiem nie obsługuje wyjątku, a bieżący try
blok jest zagnieżdżony w innych try
blokach w bieżącym wywołaniu, catch
bloki skojarzone z następnym otaczającym try
blokiem są przeszukiwane. Jeśli nie catch
zostanie znaleziony żaden blok dla wyjątku, system wyszukuje poprzednie poziomy zagnieżdżenia w bieżącym wywołaniu. Jeśli w bieżącym wywołaniu nie catch
zostanie znaleziony żaden blok wyjątku, wyjątek zostanie przekazany do stosu wywołań, a poprzednia ramka stosu zostanie przeszukana pod kątem bloku obsługującego catch
wyjątek. Wyszukiwanie stosu wywołań będzie kontynuowane do momentu obsługi wyjątku lub do momentu, aż w stosie wywołań nie istnieją żadne ramki. Jeśli górna część stosu wywołań zostanie osiągnięta bez znalezienia bloku obsługującego catch
wyjątek, domyślna procedura obsługi wyjątków obsługuje go i aplikacja kończy działanie.
Wypróbuj język F#. z wyrażeniem
Język F# nie używa catch
bloków. Zamiast tego zgłoszony wyjątek jest dopasowywany do wzorca przy użyciu pojedynczego with
bloku. Ponieważ jest to wyrażenie, a nie instrukcja, wszystkie ścieżki muszą zwracać ten sam typ. Aby dowiedzieć się więcej, zobacz Try... z wyrażeniem.
Funkcje typu wyjątku
Typy wyjątków obsługują następujące funkcje:
Czytelny dla człowieka tekst opisujący błąd. W przypadku wystąpienia wyjątku środowisko uruchomieniowe udostępnia komunikat tekstowy informujący użytkownika o charakterze błędu i sugerowanie akcji w celu rozwiązania problemu. Ta wiadomość tekstowa jest przechowywana we Message właściwości obiektu wyjątku. Podczas tworzenia obiektu wyjątku można przekazać ciąg tekstowy do konstruktora, aby opisać szczegóły tego konkretnego wyjątku. Jeśli do konstruktora nie podano argumentu komunikatu o błędzie, zostanie użyty domyślny komunikat o błędzie. Aby uzyskać więcej informacji, zobacz Message właściwość .
Stan stosu wywołań, gdy wyjątek został zgłoszony. Właściwość StackTrace zawiera ślad stosu, którego można użyć do określenia, gdzie występuje błąd w kodzie. Ślad stosu zawiera listę wszystkich wywoływanych metod i numerów wierszy w pliku źródłowym, w którym są wykonywane wywołania.
Właściwości klasy wyjątku
Klasa Exception zawiera wiele właściwości, które pomagają zidentyfikować lokalizację kodu, typ, plik pomocy i przyczynę wyjątku: StackTrace, HResultInnerExceptionHelpLinkSourceMessageTargetSitei .Data
Gdy relacja przyczynowa istnieje między co najmniej dwoma wyjątkami, InnerException właściwość przechowuje te informacje. Wyjątek zewnętrzny jest zgłaszany w odpowiedzi na ten wyjątek wewnętrzny. Kod, który obsługuje wyjątek zewnętrzny, może używać informacji z wcześniejszego wyjątku wewnętrznego w celu bardziej odpowiedniego obsługi błędu. Dodatkowe informacje o wyjątku mogą być przechowywane jako kolekcja par klucz/wartość we Data właściwości .
Ciąg komunikatu o błędzie przekazywany do konstruktora podczas tworzenia obiektu wyjątku powinien być zlokalizowany i można go dostarczyć z pliku zasobu przy użyciu ResourceManager klasy . Aby uzyskać więcej informacji na temat zlokalizowanych zasobów, zobacz Tworzenie zestawów satelickich i tworzenie pakietów i wdrażanie zasobów .
Aby udostępnić użytkownikowi obszerne informacje o przyczynie wystąpienia wyjątku, HelpLink właściwość może przechowywać adres URL (lub URN) w pliku pomocy.
Klasa Exception używa wartości HRESULT COR_E_EXCEPTION
, która ma wartość 0x80131500.
Aby uzyskać listę początkowych wartości właściwości dla wystąpienia Exception klasy, zobacz Exception konstruktory.
Zagadnienia dotyczące wydajności
Zgłaszanie lub obsługa wyjątku zużywa znaczną ilość zasobów systemowych i czasu wykonywania. Zgłaszaj wyjątki tylko do obsługi naprawdę nadzwyczajnych warunków, a nie do obsługi przewidywalnych zdarzeń ani sterowania przepływem. Na przykład w niektórych przypadkach, takich jak podczas tworzenia biblioteki klas, uzasadnione jest zgłoszenie wyjątku, jeśli argument metody jest nieprawidłowy, ponieważ oczekujesz, że metoda zostanie wywołana z prawidłowymi parametrami. Nieprawidłowy argument metody, jeśli nie jest wynikiem błędu użycia, oznacza, że wystąpił coś niezwykłego. Z drugiej strony nie zgłaszaj wyjątku, jeśli dane wejściowe użytkownika są nieprawidłowe, ponieważ użytkownicy mogą od czasu do czasu wprowadzać nieprawidłowe dane. Zamiast tego podaj mechanizm ponawiania prób, aby użytkownicy mogli wprowadzać prawidłowe dane wejściowe. Nie należy też używać wyjątków do obsługi błędów użycia. Zamiast tego użyj asercji , aby zidentyfikować i poprawić błędy użycia.
Ponadto nie zgłaszaj wyjątku, gdy kod powrotny jest wystarczający; nie konwertuj kodu powrotnego na wyjątek; i nie należy rutynowo przechwytywać wyjątku, ignorować go, a następnie kontynuować przetwarzanie.
Ponowne zgłaszanie wyjątku
W wielu przypadkach program obsługi wyjątków po prostu chce przekazać wyjątek do elementu wywołującego. Najczęściej występuje to w:
Biblioteka klas, która z kolei opakowuje wywołania metod w bibliotece klas platformy .NET lub innych bibliotekach klas.
Aplikacja lub biblioteka, która napotka wyjątek krytyczny. Procedura obsługi wyjątków może zarejestrować wyjątek, a następnie ponownie zgłosić wyjątek.
Zalecanym sposobem ponownego zgłaszania wyjątku jest po prostu użycie instrukcji throw w języku C#, funkcji reraise w języku F# i instrukcji Throw w Visual Basic bez uwzględniania wyrażenia. Dzięki temu wszystkie informacje o stosie wywołań są zachowywane po propagacji wyjątku do elementu wywołującego. Ilustruje to poniższy przykład. Metoda rozszerzenia ciągu, FindOccurrences
, opakowuje jedno lub więcej wywołań bez String.IndexOf(String, Int32) sprawdzania poprawności argumentów wcześniej.
using System;
using System.Collections.Generic;
public static class Library1
{
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)
{
// 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 FindOccurrences1(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
Następnie wywołujący wywołuje FindOccurrences
dwa razy. W drugim wywołaniu metody FindOccurrences
obiekt wywołujący przekazuje null
element jako ciąg wyszukiwania, co powoduje String.IndexOf(String, Int32) , że metoda zgłasza ArgumentNullException wyjątek. Ten wyjątek jest obsługiwany przez metodę FindOccurrences
i przekazywany z powrotem do elementu wywołującego. Ponieważ instrukcja throw jest używana bez wyrażenia, dane wyjściowe z przykładu pokazują, że stos wywołań jest zachowywany.
public class RethrowEx1
{
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 Example1
Public Sub Main()
Dim s As String = "It was a cold day when..."
Dim indexes() As Integer = s.FindOccurrences1("a")
ShowOccurrences(s, "a", indexes)
Console.WriteLine()
Dim toFind As String = Nothing
Try
indexes = s.FindOccurrences1(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()
W przeciwieństwie do tego, jeśli wyjątek jest zgłaszany ponownie przy użyciu tej instrukcji:
throw e;
Throw e
raise e
... następnie pełny stos wywołań nie jest zachowywany, a przykład generuje następujące dane wyjściowe:
'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()
Nieco bardziej uciążliwą alternatywą jest zgłoszenie nowego wyjątku i zachowanie informacji o stosie wywołań oryginalnego wyjątku w wyjątku wewnętrznym. Obiekt wywołujący może następnie użyć właściwości nowego wyjątku InnerException , aby pobrać ramkę stosu i inne informacje o oryginalnym wyjątku. W takim przypadku instrukcja throw to:
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)
Kod użytkownika, który obsługuje wyjątek, musi wiedzieć, że InnerException właściwość zawiera informacje o oryginalnym wyjątku, jak pokazano w poniższym procedurze obsługi wyjątków.
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)
Wybieranie standardowych wyjątków
Jeśli musisz zgłosić wyjątek, często można użyć istniejącego typu wyjątku na platformie .NET zamiast implementowania wyjątku niestandardowego. Należy użyć standardowego typu wyjątku w następujących dwóch warunkach:
Zgłaszasz wyjątek spowodowany błędem użycia (czyli błędem logiki programu wywoływanym przez dewelopera, który wywołuje metodę). Zazwyczaj zgłaszany jest wyjątek, taki jak ArgumentException, ArgumentNullException, InvalidOperationExceptionlub NotSupportedException. Ciąg podany w konstruktorze obiektu wyjątku podczas tworzenia wystąpienia obiektu wyjątku powinien opisać błąd, aby deweloper mógł go naprawić. Aby uzyskać więcej informacji, zobacz Message właściwość .
Obsługujesz błąd, który można przekazać do wywołującego przy użyciu istniejącego wyjątku platformy .NET. Należy zgłosić najbardziej możliwy wyjątek pochodny. Jeśli na przykład metoda wymaga, aby argument był prawidłowym elementem członkowskim typu wyliczenia, należy zgłosić (najbardziej pochodną klasę InvalidEnumArgumentException ) zamiast ArgumentException.
W poniższej tabeli wymieniono typowe typy wyjątków i warunki, w których można je zgłosić.
Wyjątek | Stan |
---|---|
ArgumentException | Argument inny niż null przekazywany do metody jest nieprawidłowy. |
ArgumentNullException | Argument przekazywany do metody to null . |
ArgumentOutOfRangeException | Argument znajduje się poza zakresem prawidłowych wartości. |
DirectoryNotFoundException | Część ścieżki katalogu jest nieprawidłowa. |
DivideByZeroException | Mianownik w klasie całkowitej lub Decimal operacji dzielenia wynosi zero. |
DriveNotFoundException | Dysk jest niedostępny lub nie istnieje. |
FileNotFoundException | Plik nie istnieje. |
FormatException | Wartość nie jest w odpowiednim formacie, który ma zostać przekonwertowany z ciągu za pomocą metody konwersji, takiej jak Parse . |
IndexOutOfRangeException | Indeks znajduje się poza granicami tablicy lub kolekcji. |
InvalidOperationException | Wywołanie metody jest nieprawidłowe w bieżącym stanie obiektu. |
KeyNotFoundException | Nie można odnaleźć określonego klucza dostępu do elementu członkowskiego w kolekcji. |
NotImplementedException | Metoda lub operacja nie jest zaimplementowana. |
NotSupportedException | Metoda lub operacja nie jest obsługiwana. |
ObjectDisposedException | Operacja jest wykonywana na obiekcie, który został usunięty. |
OverflowException | Operacja arytmetyczna, rzutowania lub konwersji powoduje przepełnienie. |
PathTooLongException | Długość ścieżki lub pliku przekracza maksymalną długość zdefiniowaną przez system. |
PlatformNotSupportedException | Operacja nie jest obsługiwana na bieżącej platformie. |
RankException | Tablica z nieprawidłową liczbą wymiarów jest przekazywana do metody. |
TimeoutException | Interwał czasu przydzielony do operacji wygasł. |
UriFormatException | Jest używany nieprawidłowy identyfikator URI (Uniform Resource Identifier). |
Implementowanie wyjątków niestandardowych
W następujących przypadkach użycie istniejącego wyjątku platformy .NET do obsługi warunku błędu nie jest odpowiednie:
Gdy wyjątek odzwierciedla unikatowy błąd programu, którego nie można zamapować na istniejący wyjątek platformy .NET.
Jeśli wyjątek wymaga obsługi innej niż obsługa, która jest odpowiednia dla istniejącego wyjątku platformy .NET, lub wyjątek musi być niejednoznaczny z podobnego wyjątku. Jeśli na przykład zgłosisz ArgumentOutOfRangeException wyjątek podczas analizowania reprezentacji liczbowej ciągu, który jest poza zakresem docelowego typu całkowitego, nie chcesz używać tego samego wyjątku dla błędu, który wynika z obiektu wywołującego nie dostarcza odpowiednich wartości ograniczonych podczas wywoływania metody.
Klasa Exception jest klasą bazową wszystkich wyjątków na platformie .NET. Wiele klas pochodnych opiera się na odziedziczonym zachowaniu składowych Exception klasy; nie zastępują składowych Exceptionklasy ani nie definiują żadnych unikatowych składowych.
Aby zdefiniować własną klasę wyjątków:
Zdefiniuj klasę dziedziczą z Exceptionklasy . W razie potrzeby zdefiniuj wszelkie unikatowe elementy członkowskie wymagane przez klasę, aby podać dodatkowe informacje o wyjątku. Na przykład ArgumentException klasa zawiera właściwość określającą ParamName nazwę parametru, którego argument spowodował wyjątek, a RegexMatchTimeoutException właściwość zawiera właściwość wskazującą MatchTimeout interwał limitu czasu.
W razie potrzeby przesłoń wszystkie dziedziczone elementy członkowskie, których funkcjonalność chcesz zmienić lub zmodyfikować. Należy pamiętać, że większość istniejących klas pochodnych Exception nie zastępuje zachowania odziedziczonych elementów członkowskich.
Ustal, czy niestandardowy obiekt wyjątku jest możliwy do serializacji. Serializacja umożliwia zapisywanie informacji o wyjątku i zezwala na udostępnianie informacji o wyjątkach przez serwer i serwer proxy klienta w kontekście komunikacji zdalnie. Aby obiekt wyjątku był serializowalny, oznacz go atrybutem SerializableAttribute .
Zdefiniuj konstruktory klasy wyjątków. Zazwyczaj klasy wyjątków mają co najmniej jeden z następujących konstruktorów:
Exception(), który używa wartości domyślnych do inicjowania właściwości nowego obiektu wyjątku.
Exception(String), który inicjuje nowy obiekt wyjątku z określonym komunikatem o błędzie.
Exception(String, Exception), który inicjuje nowy obiekt wyjątku z określonym komunikatem o błędzie i wyjątkiem wewnętrznym.
Exception(SerializationInfo, StreamingContext), który jest konstruktorem
protected
, który inicjuje nowy obiekt wyjątku z serializowanych danych. Należy zaimplementować ten konstruktor, jeśli wybrano opcję serializacji obiektu wyjątku.
Poniższy przykład ilustruje użycie niestandardowej klasy wyjątków. Definiuje wyjątek NotPrimeException
zgłaszany, gdy klient próbuje pobrać sekwencję liczb pierwszych, określając liczbę początkową, która nie jest podstawowa. Wyjątek definiuje nową właściwość , NonPrime
która zwraca liczbę inną niż prime, która spowodowała wyjątek. Oprócz implementowania chronionego konstruktora bez parametrów i konstruktora z parametrami SerializationInfo i StreamingContext do serializacji NotPrimeException
, klasa definiuje trzy dodatkowe konstruktory do obsługi NonPrime
właściwości. Każdy konstruktor wywołuje konstruktor klasy bazowej oprócz zachowania wartości liczby innej niż prime. Klasa NotPrimeException
jest również oznaczona atrybutem 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
Klasa pokazana PrimeNumberGenerator
w poniższym przykładzie używa sita Eratosthenes do obliczania sekwencji liczb pierwszych z 2 do limitu określonego przez klienta w wywołaniu konstruktora klasy. Metoda GetPrimesFrom
zwraca wszystkie liczby pierwsze, które są większe lub równe określonej niższej granicy, ale zwraca NotPrimeException
wartość , jeśli ta niższa granica nie jest liczbą pierwszą.
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
Poniższy przykład wykonuje dwa wywołania GetPrimesFrom
metody z liczbami innych niż prime, z których jeden przekracza granice domeny aplikacji. W obu przypadkach wyjątek jest zgłaszany i pomyślnie obsługiwany w kodzie klienta.
using System;
using System.Reflection;
class Example1
{
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()
' --------
Przykłady
W poniższym przykładzie pokazano catch
blok (with
w języku F#), który jest zdefiniowany do obsługi ArithmeticException błędów. Ten catch
blok przechwytuje DivideByZeroException również błędy, ponieważ DivideByZeroException pochodzi z ArithmeticException i nie ma catch
jawnie zdefiniowanego bloku dla DivideByZeroException błędów.
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()
'