System.Exception-Klasse
Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.
Die Exception Klasse ist die Basisklasse für alle Ausnahmen. Wenn ein Fehler auftritt, meldet das System oder die aktuell ausgeführte Anwendung diesen, indem eine Ausnahme ausgelöst wird, die Informationen zum Fehler enthält. Nachdem eine Ausnahme ausgelöst wurde, wird sie von der Anwendung oder vom Standardausnahmehandler behandelt.
Fehler und Ausnahmen
Laufzeitfehler können aus verschiedenen Gründen auftreten. Nicht alle Fehler sollten jedoch als Ausnahmen in Ihrem Code behandelt werden. Nachfolgend finden Sie einige Fehlerkategorien, die zur Laufzeit auftreten können, und die geeigneten Möglichkeiten, auf sie zu reagieren.
Verwendungsfehler: Ein Verwendungsfehler stellt einen Fehler in der Programmlogik dar, der zu einer Ausnahme führen kann. Der Fehler sollte jedoch nicht durch die Ausnahmebehandlung behoben werden, sondern durch die Korrektur des fehlerhaften Codes. Die Außerkraftsetzung der Object.Equals(Object)-Methode im folgenden Beispiel geht beispielsweise davon aus, dass das Argument
obj
nie NULL sein darf.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
Die NullReferenceException-Ausnahme, die entsteht, wenn
obj
gleichnull
ist, kann vermieden werden, indem Sie den Quellcode so ändern, dass er explizit auf NULL testet, bevor die Object.Equals-Außerkraftsetzung aufgerufen wird, und der Code dann erneut kompiliert wird. Das folgende Beispiel enthält den korrigierten Quellcode, der einnull
-Argument verarbeitet.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
Anstatt die Ausnahmebehandlung für Verwendungsfehler zu verwenden, können Sie die Debug.Assert-Methode verwenden, um Verwendungsfehler in Debugbuilds zu ermitteln, und die Trace.Assert-Methode, um Verwendungsfehler in Debug- und Releasebuilds zu ermitteln. Weitere Informationen finden Sie unter Assertionen in verwaltetem Code.
Programmfehler: Ein Programmfehler ist ein Laufzeitfehler, der nicht immer durch Schreiben von fehlerfreiem Code vermieden werden kann.
In einigen Fällen kann ein Programmfehler eine erwartete oder routinemäßige Fehlerbedingung darstellen. In diesem Fall sollten Sie die Verwendung der Ausnahmebehandlung vermeiden, um den Programmfehler zu behandeln, und den Vorgang stattdessen wiederholen. Wenn beispielsweise erwartet wird, dass der oder die Benutzer*in ein Datum in einem bestimmten Format eingibt, können Sie die Datumszeichenfolge parsen, indem Sie die DateTime.TryParseExact-Methode aufrufen, die einen Boolean-Wert zurückgibt. Dieser gibt an, ob der Parsingvorgang erfolgreich war, anstatt die DateTime.ParseExact-Methode zu verwenden, wodurch eine FormatException-Ausnahme ausgelöst wird, wenn die Datumszeichenfolge nicht in einen DateTime-Wert konvertiert werden kann. Wenn ein*e Benutzer*in versucht, eine Datei zu öffnen, die nicht vorhanden ist, können Sie zuerst die File.Exists-Methode aufrufen, um zu überprüfen, ob die Datei vorhanden ist. Wenn das nicht der Fall ist, wird der oder die Benutzer*in gefragt, ob die Datei erstellt werden soll.
In anderen Fällen spiegelt ein Programmfehler eine unerwartete Fehlerbedingung wider, die in Ihrem Code behandelt werden kann. Selbst wenn Sie überprüft haben, ob eine Datei vorhanden ist, könnte sie gelöscht worden sein, bevor Sie sie öffnen konnten, oder sie ist beschädigt. In diesem Fall wird beim Versuch, die Datei zu öffnen, indem ein StreamReader-Objekt instanziiert oder die Open-Methode aufgerufen wird, möglicherweise eine FileNotFoundException-Ausnahme ausgelöst. In diesen Fällen sollten Sie die Ausnahmebehandlung verwenden, um den Fehler zu korrigieren.
Systemfehler: Ein Systemfehler ist ein Laufzeitfehler, der programmgesteuert nicht effizient behandelt werden kann. Beispielsweise kann jede Methode eine OutOfMemoryException-Ausnahme auslösen, wenn die Common Language Runtime keinen zusätzlichen Arbeitsspeicher belegen kann. In der Regel werden Systemfehler nicht mithilfe der Ausnahmebehandlung behandelt. Stattdessen können Sie möglicherweise ein Ereignis wie AppDomain.UnhandledException verwenden und die Environment.FailFast-Methode aufrufen, um Ausnahmeinformationen zu protokollieren und den Benutzer oder die Benutzerin über den Fehler zu benachrichtigen, bevor die Anwendung beendet wird.
try/catch-Blöcke
Die Common Language Runtime stellt ein Ausnahmebehandlungsmodell bereit, das auf der Darstellung von Ausnahmen als Objekte und der Trennung von Programmcode und Ausnahmebehandlungscode in try
-Blöcke und catch
-Blöcke basiert. Es kann einen oder mehrere catch
-Blöcke geben, die jeweils für die Behandlung eines bestimmten Ausnahmetyps vorgesehen sind, oder einen Block, der eine spezifischere Ausnahme als ein anderer Block abfangen soll.
Wenn eine Anwendung Ausnahmen behandelt, die während der Ausführung eines Anwendungscodeblocks auftreten, muss der Code in einer try
-Anweisung platziert werden. Dieser Code wird als try
-Block bezeichnet. Anwendungscode, der Ausnahmen behandelt, die von einem try
-Block ausgelöst werden, wird in einer catch
-Anweisung platziert und als catch
-Block bezeichnet. Null oder mehr catch
-Blöcke werden einem try
-Block zugeordnet, und jeder catch
-Block enthält einen Typfilter, der die von ihm behandelten Ausnahmen bestimmt.
Wenn eine Ausnahme in einem try
-Block auftritt, durchsucht das System die zugeordneten catch
-Blöcke in der Reihenfolge, in der sie im Anwendungscode vorkommen, bis ein catch
-Block gefunden wird, der die Ausnahme behandelt. Ein catch
-Block behandelt eine Ausnahme vom Typ T
, wenn der Typfilter des catch-Blocks T
oder einen beliebigen Typ angibt, von dem T
abgeleitet wird. Das System beendet die Suche, nachdem der erste catch
-Block gefunden wurde, der die Ausnahme behandelt. Aus diesem Grund muss im Anwendungscode ein catch
-Block, der einen Typ verarbeitet, vor einem catch
-Block angegeben werden, der seine Basistypen verarbeitet. Dies wird im folgenden Beispiel veranschaulicht. Ein catch-Block, der System.Exception
behandelt, wird zuletzt angegeben.
Wenn keine der catch
-Blöcke, die dem aktuellen try
-Block zugeordnet sind, die Ausnahme behandeln, und der aktuelle try
-Block in anderen try
-Blöcken im aktuellen Aufruf geschachtelt ist, werden die catch
-Blöcke durchsucht, die dem nächsten eingeschlossenen try
-Block zugeordnet sind. Wenn kein catch
-Block für die Ausnahme gefunden wird, durchsucht das System vorherige Schachtelungsebenen im aktuellen Aufruf. Wenn im aktuellen Aufruf kein catch
-Block für die Ausnahme gefunden wird, wird die Ausnahme an die Aufrufliste übergeben, und der vorherige Stapelrahmen wird nach einem catch
-Block durchsucht, der die Ausnahme behandelt. Das Durchsuchen der Aufrufliste wird fortgesetzt, bis die Ausnahme behandelt wird oder keine weiteren Frames in der Aufrufliste vorhanden sind. Wenn das obere Ende der Aufrufliste erreicht ist und kein catch
-Block gefunden wurde, der die Ausnahme behandelt, behandelt der Standardausnahmehandler sie, und die Anwendung wird beendet.
try..with-Ausdruck (F#)
F# verwendet keine catch
-Blöcke. Stattdessen wird eine ausgelöste Ausnahme mit einem einzelnen with
-Block abgeglichen. Da es sich um einen Ausdruck statt um eine Anweisung handelt, müssen alle Pfade denselben Typ zurückgeben. Weitere Informationen finden Sie unter try...with-Ausdruck.
Features für Ausnahmetypen
Ausnahmetypen unterstützen die folgenden Features:
Lesbarer Text, der den Fehler beschreibt: Wenn eine Ausnahme auftritt, stellt die Runtime eine Textnachricht zur Verfügung, um den oder die Benutzer*in über die Art des Fehlers zu informieren und Maßnahmen zur Behebung des Problems vorzuschlagen. Diese Textnachricht wird in der Message-Eigenschaft des Ausnahmeobjekts gespeichert. Beim Erstellen des Ausnahmeobjekts können Sie eine Textzeichenfolge an den Konstruktor übergeben, um die Details dieser Ausnahme zu beschreiben. Wenn dem Konstruktor kein Fehlermeldungsargument übergeben wird, wird die Standardfehlermeldung verwendet. Weitere Informationen finden Sie in den Ausführungen zur Message-Eigenschaft.
Der Status des Aufrufstapels, als die Ausnahme ausgelöst wurde: Die StackTrace-Eigenschaft enthält eine Stapelüberwachung, mit der bestimmt werden kann, wo der Fehler im Code auftritt. Die Stapelüberwachung listet alle aufgerufenen Methoden und die Zeilennummern in der Quelldatei auf, in der die Aufrufe getätigt werden.
Eigenschaften von Ausnahmeklassen
Die Exception-Klasse enthält eine Reihe von Eigenschaften, mit denen der Codespeicherort, der Typ, die Hilfedatei und der Grund für die Ausnahme ermittelt werden können: StackTrace, InnerException, Message, HelpLink, HResult, Source, TargetSite und Data.
Wenn eine kausale Beziehung zwischen zwei oder mehr Ausnahmen besteht, verwaltet die InnerException-Eigenschaft diese Informationen. Die äußere Ausnahme wird als Reaktion auf diese innere Ausnahme ausgelöst. Der Code, der die äußere Ausnahme behandelt, kann die Informationen aus der früheren inneren Ausnahme verwenden, um den Fehler besser zu behandeln. Ergänzende Informationen zur Ausnahme können als Collection von Schlüssel-Wert-Paaren in der Eigenschaft Data gespeichert werden.
Die Fehlermeldungszeichenfolge, die während der Erstellung des Ausnahmeobjekts an den Konstruktor übergeben wird, sollte lokalisiert werden und kann mithilfe der ResourceManager-Klasse aus einer Ressourcendatei bereitgestellt werden. Weitere Informationen zu lokalisierten Ressourcen finden Sie in den Artikeln Erstellen von Satellitenassemblys und Packen und Bereitstellen von Ressourcen.
Um Benutzer*innen umfangreiche Informationen darüber zu liefern, warum die Ausnahme aufgetreten ist, kann die HelpLink-Eigenschaft eine URL (oder URN) zu einer Hilfedatei enthalten.
Die Exception-Klasse verwendet HRESULT COR_E_EXCEPTION
mit dem Wert 0x80131500.
Eine Liste der anfänglichen Eigenschaftenwerte für eine Instanz der Exception-Klasse finden Sie in den Exception-Konstruktoren.
Überlegungen zur Leistung
Das Auslösen oder Behandeln einer Ausnahme verbraucht eine erhebliche Menge an Systemressourcen und benötigt eine lange Ausführungszeit. Lösen Sie Ausnahmen nur aus, um wirklich außergewöhnliche Bedingungen zu behandeln, nicht für vorhersehbare Ereignisse oder die Ablaufsteuerung. In einigen Fällen, z. B. beim Entwickeln einer Klassenbibliothek, ist es sinnvoll, eine Ausnahme auszulösen, wenn ein Methodenargument ungültig ist, da Sie erwarten, dass die Methode mit gültigen Parametern aufgerufen wird. Ein ungültiges Methodenargument, wenn es nicht das Ergebnis eines Verwendungsfehlers ist, bedeutet, dass etwas Außergewöhnliches aufgetreten ist. Lösen Sie dagegen keine Ausnahme aus, wenn die Benutzereingabe ungültig ist, da zu erwarten ist, dass Benutzer*innen gelegentlich ungültige Daten eingeben. Implementieren Sie stattdessen einen Wiederholungsmechanismus, damit Benutzer*innen gültige Eingaben machen können. Sie sollten auch keine Ausnahmen verwenden, um Verwendungsfehler zu behandeln. Verwenden Sie stattdessen Assertionen, um Verwendungsfehler zu ermitteln und zu beheben.
Lösen Sie außerdem keine Ausnahme aus, wenn ein Rückgabecode ausreichend ist. Konvertieren Sie keinen Rückgabecode in eine Ausnahme, und fangen Sie Ausnahmen nicht routinemäßig ab, ignorieren sie und fahren dann mit der Verarbeitung fort.
Erneutes Auslösen einer Ausnahme
In vielen Fällen soll ein Ausnahmehandler die Ausnahme nur an den Aufrufer übergeben. Das ist am häufigsten der Fall bei:
Eine Klassenbibliothek, die wiederum Aufrufe von Methoden in der .NET-Klassenbibliothek oder anderen Klassenbibliotheken umschließt
Eine Anwendung oder Bibliothek, die auf eine schwerwiegende Ausnahme stößt. Der Ausnahmehandler kann die Ausnahme protokollieren und dann die Ausnahme erneut auslösen.
Die empfohlene Methode, eine Ausnahme erneut auszulösen, besteht darin, die throw-Anweisung in C# zu verwenden, die reraise-Funktion in F# oder die Throw-Anweisung ohne Ausdruck in Visual Basic. Dadurch wird sichergestellt, dass alle Aufruflisteninformationen beibehalten werden, wenn die Ausnahme an den Aufrufer weitergegeben wird. Dies wird anhand des folgenden Beispiels veranschaulicht. Eine Zeichenfolgenerweiterungsmethode, FindOccurrences
, umschließt einen oder mehrere Aufrufe von String.IndexOf(String, Int32), ohne die Argumente vorher zu überprüfen.
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
Ein Aufrufer ruft dann zweimal FindOccurrences
auf. Im zweiten Aufruf von FindOccurrences
übergibt der Aufrufer null
als Suchzeichenfolge, wodurch die String.IndexOf(String, Int32)-Methode eine ArgumentNullException-Ausnahme auslöst. Diese Ausnahme wird von der FindOccurrences
-Methode behandelt und an den Aufrufer übergeben. Da die throw-Anweisung ohne Ausdruck verwendet wird, zeigt die Ausgabe aus dem Beispiel, dass die Aufrufliste beibehalten wird.
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()
Wenn die Ausnahme jedoch mithilfe dieser Anweisung erneut ausgelöst wird:
throw e;
Throw e
raise e
... dann wird die vollständige Aufrufliste nicht beibehalten, und das Beispiel würde die folgende Ausgabe generieren:
'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()
Eine etwas umständlichere Alternative besteht darin, eine neue Ausnahme auszulösen und die Informationen zur Aufrufliste der ursprünglichen Ausnahme in einer inneren Ausnahme beizubehalten. Der Aufrufer kann dann die InnerException-Eigenschaft der neuen Ausnahme verwenden, um Stapelrahmen und andere Informationen zur ursprünglichen Ausnahme abzurufen. In diesem Fall lautet die throw-Anweisung:
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)
Der Benutzercode, der die Ausnahme behandelt, muss wissen, dass die InnerException-Eigenschaft Informationen zur ursprünglichen Ausnahme enthält, wie der folgende Ausnahmehandler veranschaulicht.
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)
Auswählen von Standardausnahmen
Wenn Sie eine Ausnahme auslösen müssen, können Sie häufig einen vorhandenen Ausnahmetyp in .NET verwenden, anstatt eine benutzerdefinierte Ausnahme zu implementieren. Sie sollten unter diesen beiden Bedingungen einen Standardausnahmetyp verwenden:
Sie lösen eine Ausnahme aus, die durch einen Verwendungsfehler verursacht wird (d. h. durch einen Fehler in der Programmlogik des Entwicklers oder der Entwicklerin, der oder die Ihre Methode aufruft). In der Regel würden Sie eine Ausnahme wie ArgumentException, ArgumentNullException, InvalidOperationException oder NotSupportedException auslösen. Die Zeichenfolge, die Sie beim Instanziieren des Ausnahmeobjekts für den Konstruktor des Ausnahmeobjekts angeben, sollte den Fehler beschreiben, damit der oder die Entwickler*in ihn beheben kann. Weitere Informationen finden Sie in den Ausführungen zur Message-Eigenschaft.
Sie behandeln einen Fehler, der dem Aufrufer mit einer vorhandenen .NET-Ausnahme mitgeteilt werden kann. Sie sollten die am häufigsten abgeleitete Ausnahme auslösen. Wenn eine Methode z. B. ein Argument als gültiges Element eines Enumerationstyps erfordert, sollten Sie InvalidEnumArgumentException (die am häufigsten abgeleitete Klasse) anstelle von ArgumentException auslösen.
In der folgenden Tabelle sind allgemeine Ausnahmetypen und die Bedingungen aufgeführt, unter denen Sie sie auslösen würden.
Exception | Bedingung |
---|---|
ArgumentException | Ein Nicht-NULL-Argument, das an eine Methode übergeben wird, ist ungültig. |
ArgumentNullException | Ein Argument, das an eine Methode übergeben wird, ist null . |
ArgumentOutOfRangeException | Ein Argument liegt außerhalb des Bereichs gültiger Werte. |
DirectoryNotFoundException | Ein Teil eines Verzeichnispfads ist ungültig. |
DivideByZeroException | Der Nenner in einem Divisionsvorgang mit Integer- oder Decimal-Werten ist null. |
DriveNotFoundException | Ein Laufwerk ist nicht verfügbar oder nicht vorhanden. |
FileNotFoundException | Eine Datei ist nicht vorhanden. |
FormatException | Ein Wert weist kein geeignetes Format auf, um durch eine Konvertierungsmethode wie Parse aus einer Zeichenfolge konvertiert zu werden. |
IndexOutOfRangeException | Ein Index liegt außerhalb der Grenzen eines Arrays oder einer Collection. |
InvalidOperationException | Ein Methodenaufruf ist im aktuellen Zustand eines Objekts ungültig. |
KeyNotFoundException | Der angegebene Schlüssel für den Zugriff auf ein Element in einer Collection kann nicht gefunden werden. |
NotImplementedException | Eine Methode oder ein Vorgang ist nicht implementiert. |
NotSupportedException | Eine Methode oder ein Vorgang wird nicht unterstützt. |
ObjectDisposedException | Ein Vorgang wird für ein Objekt ausgeführt, das verworfen wurde. |
OverflowException | Ein arithmetischer, Umwandlungs- oder Konvertierungsvorgang führt zu einem Überlauf. |
PathTooLongException | Ein Pfad oder Dateiname überschreitet die maximale systemdefinierte Länge. |
PlatformNotSupportedException | Der Vorgang wird auf der aktuellen Plattform nicht unterstützt. |
RankException | Ein Array mit der falschen Anzahl von Dimensionen wird an eine Methode übergeben. |
TimeoutException | Das Zeitintervall, das einem Vorgang zugewiesen ist, ist abgelaufen. |
UriFormatException | Es wird ein ungültiger URI (Uniform Resource Identifier) verwendet. |
Implementieren von benutzerdefinierten Ausnahmen
In den folgenden Fällen ist die Verwendung einer vorhandenen .NET-Ausnahme zur Behandlung einer Fehlerbedingung nicht ausreichend:
Wenn die Ausnahme einen eindeutigen Programmfehler widerspiegelt, der keiner vorhandenen .NET-Ausnahme zugeordnet werden kann
Wenn für die Ausnahme eine Behandlung erforderlich ist, die sich von der Behandlung unterscheidet, die für eine vorhandene .NET-Ausnahme geeignet ist, oder die Ausnahme von einer ähnlichen Ausnahme getrennt werden muss. Wenn Sie beispielsweise beim Analysieren der numerischen Darstellung einer Zeichenfolge, die außerhalb des Bereichs des Zielintegraltyps liegt, eine ArgumentOutOfRangeException-Ausnahme auslösen, sollten Sie beim Aufrufen der Methode nicht dieselbe Ausnahme für einen Fehler verwenden, der aus dem Aufrufer resultiert, ohne die entsprechenden eingeschränkten Werte anzugeben.
Die Exception-Klasse ist die Basisklasse aller Ausnahmen in .NET. Viele abgeleitete Klassen basieren auf dem geerbten Verhalten der Member der Exception.Klasse. Sie überschreiben weder die Member von Exception noch definieren sie eindeutige Member.
So definieren Sie Ihre eigene Ausnahmeklasse:
Definieren Sie eine Klasse, die von Exception erbt. Definieren Sie bei Bedarf alle eindeutigen Member, die von Ihrer Klasse benötigt werden, um zusätzliche Informationen zur Ausnahme anzugeben. Die ArgumentException-Klasse enthält beispielsweise eine ParamName-Eigenschaft, die den Namen des Parameters angibt, dessen Argument die Ausnahme verursacht hat, und die RegexMatchTimeoutException-Eigenschaft enthält eine MatchTimeout-Eigenschaft, die das Timeoutintervall angibt.
Setzen Sie bei Bedarf alle geerbten Member außer Kraft, deren Funktionalität Sie ändern möchten. Beachten Sie, dass die meisten vorhandenen abgeleiteten Klassen von Exception das Verhalten von geerbten Membern nicht außer Kraft setzen.
Bestimmen Sie, ob Ihr benutzerdefiniertes Ausnahmeobjekt serialisierbar ist. Mithilfe der Serialisierung können Sie Informationen über die Ausnahme speichern und zulassen, dass Ausnahmeinformationen von einem Server und einem Clientproxy in einem Remotingkontext freigegeben werden. Um das Ausnahmeobjekt serialisierbar zu machen, markieren Sie es mit dem Attribut SerializableAttribute.
Definieren Sie die Konstruktoren Ihrer Ausnahmeklasse. In der Regel verfügen Ausnahmeklassen über einen oder mehrere der folgenden Konstruktoren:
Exception(), der Standardwerte verwendet, um die Eigenschaften eines neuen Ausnahmeobjekts zu initialisieren
Exception(String), der ein neues Ausnahmeobjekt mit einer angegebenen Fehlermeldung initialisiert
Exception(String, Exception), der ein neues Ausnahmeobjekt mit einer angegebenen Fehlermeldung und einer inneren Ausnahme initialisiert
Exception(SerializationInfo, StreamingContext), bei dem es sich um einen
protected
-Konstruktor handelt, der ein neues Ausnahmeobjekt aus serialisierten Daten initialisiert. Sie sollten diesen Konstruktor implementieren, wenn Sie beschlossen haben, das Ausnahmeobjekt serialisierbar zu machen.
Das folgende Beispiel veranschaulicht die Verwendung einer benutzerdefinierten Ausnahmeklasse. Es definiert eine NotPrimeException
-Ausnahme, die ausgelöst wird, wenn ein Client versucht, eine Sequenz von Primzahlen abzurufen, indem eine Anfangsnummer angegeben wird, die keine Primzahl ist. Die Ausnahme definiert eine neue Eigenschaft, NonPrime
, die die zusammengesetzte Zahl zurückgibt, die die Ausnahme verursacht hat. Neben der Implementierung eines geschützten parameterlosen Konstruktors und eines Konstruktors mit SerializationInfo- und StreamingContext-Parametern für die Serialisierung definiert die NotPrimeException
-Klasse drei zusätzliche Konstruktoren zur Unterstützung der NonPrime
-Eigenschaft. Jeder Konstruktor ruft zusätzlich zum Speichern des Werts der zusammengesetzten Zahl einen Basisklassenkonstruktor auf. Die NotPrimeException
-Klasse ist auch mit dem Attribut SerializableAttribute gekennzeichnet.
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
Die im folgenden Beispiel gezeigte PrimeNumberGenerator
-Klasse verwendet das Sieb des Eratosthenes, um die Primzahlsequenz von 2 bis zu einem Grenzwert zu berechnen, der vom Client im Aufruf des Klassenkonstruktors angegeben wird. Die GetPrimesFrom
-Methode gibt alle Primzahlen zurück, die größer oder gleich einer angegebenen Untergrenze sind, löst jedoch NotPrimeException
aus, wenn dieser untere Grenzwert keine Primzahl ist.
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
Im folgenden Beispiel werden zwei Aufrufe der GetPrimesFrom
-Methode mit zusammengesetzten Zahlen ausgeführt, von denen eine die Anwendungsdomänengrenzen überschreitet. In beiden Fällen wird die Ausnahme ausgelöst und erfolgreich im Clientcode behandelt.
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()
' --------
Beispiele
Im folgenden Beispiel wird ein catch
-Block (with
in F#) veranschaulicht, der für die Behandlung von ArithmeticException-Fehlern definiert ist. Dieser catch
-Block erfasst auch DivideByZeroException-Fehler, da DivideByZeroException von ArithmeticException abgeleitet ist und kein catch
-Block explizit für DivideByZeroException-Fehler definiert ist.
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()
'