Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
In dit artikel vindt u aanvullende opmerkingen in de referentiedocumentatie voor deze API.
De Exception klasse is de basisklasse voor alle uitzonderingen. Wanneer er een fout optreedt, meldt het systeem of de toepassing die momenteel wordt uitgevoerd, een uitzondering met informatie over de fout. Nadat een uitzondering is gegenereerd, wordt deze verwerkt door de toepassing of door de standaarduitzonderingshandler.
Fouten en uitzonderingen
Runtimefouten kunnen om verschillende redenen optreden. Niet alle fouten moeten echter worden verwerkt als uitzonderingen in uw code. Hier volgen enkele categorieën fouten die tijdens runtime kunnen optreden en de juiste manieren om erop te reageren.
Gebruiksfouten. Een gebruiksfout vertegenwoordigt een fout in programmalogica die kan resulteren in een uitzondering. De fout moet echter niet worden opgelost via uitzonderingsafhandeling, maar door de foutieve code te wijzigen. Bij het overschrijven van de Object.Equals(Object) methode in het volgende voorbeeld wordt bijvoorbeeld ervan uitgegaan dat het
objargument altijd niet null moet zijn.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: {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 ModuleDe NullReferenceException uitzondering die het resultaat is wanneer
objnullkan worden geëlimineerd door de broncode te wijzigen om expliciet te testen op null voordat de Object.Equals overschrijving wordt aangeroepen en vervolgens opnieuw te compileren. Het volgende voorbeeld bevat de gecorrigeerde broncode waarmee eennullargument wordt verwerkt.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: {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: FalsePublic 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: FalseIn plaats van uitzonderingsafhandeling te gebruiken voor gebruiksfouten, kunt u de Debug.Assert methode gebruiken om gebruiksfouten in builds voor foutopsporing te identificeren en de Trace.Assert methode om gebruiksfouten te identificeren in zowel foutopsporing als release-builds. Zie Assertions in Managed Code voor meer informatie.
Programmafouten. Een programmafout is een runtimefout die niet noodzakelijkerwijs kan worden vermeden door foutloze code te schrijven.
In sommige gevallen kan een programmafout een verwachte of routinefoutconditie weerspiegelen. In dit geval wilt u voorkomen dat uitzonderingsafhandeling wordt gebruikt om de programmafout af te handelen en in plaats daarvan de bewerking opnieuw uit te voeren. Als de gebruiker bijvoorbeeld een datum in een bepaalde notatie moet invoeren, kunt u de datumtekenreeks parseren door de DateTime.TryParseExact methode aan te roepen, die een Boolean waarde retourneert die aangeeft of de parseringsbewerking is geslaagd, in plaats van de DateTime.ParseExact methode te gebruiken, waardoor een FormatException uitzondering wordt gegenereerd als de datumtekenreeks niet kan worden geconverteerd naar een DateTime waarde. Als een gebruiker probeert een bestand te openen dat niet bestaat, kunt u eerst de File.Exists methode aanroepen om te controleren of het bestand bestaat en, als dat niet het geval is, de gebruiker vragen of het bestand moet worden gemaakt.
In andere gevallen weerspiegelt een programmafout een onverwachte foutvoorwaarde die in uw code kan worden verwerkt. Zelfs als u hebt gecontroleerd of er een bestand bestaat, kan het worden verwijderd voordat u het kunt openen of beschadigd. In dat geval kan een uitzondering van het type StreamReader optreden bij het openen van het bestand door een Open-object te instantiëren of door de FileNotFoundException-methode aan te roepen. In deze gevallen moet u uitzonderingsafhandeling gebruiken om te herstellen van de fout.
Systeemfouten. Een systeemfout is een runtimefout die niet programmatisch kan worden afgehandeld op een zinvolle manier. Elke methode kan bijvoorbeeld een OutOfMemoryException uitzondering genereren als de algemene taalruntime geen extra geheugen kan toewijzen. Normaliter worden systeemfouten niet afgehandeld met uitzonderingsafhandeling. In plaats daarvan kunt u mogelijk een gebeurtenis gebruiken, zoals AppDomain.UnhandledException en de Environment.FailFast methode aanroepen om uitzonderingsgegevens vast te leggen en de gebruiker op de hoogte te stellen van de fout voordat de toepassing wordt beëindigd.
Blokken proberen/vangen
De algemene taalruntime biedt een uitzonderingsverwerkingsmodel dat is gebaseerd op de weergave van uitzonderingen als objecten en de scheiding van programmacode en uitzonderingsverwerkingscode in try blokken en catch blokken. Er kunnen een of meer catch blokken zijn, die elk zijn ontworpen voor het afhandelen van een bepaald type uitzondering of één blok dat is ontworpen om een specifiekere uitzondering te ondervangen dan een ander blok.
Als een toepassing uitzonderingen afhandelt die optreden tijdens het uitvoeren van een blok toepassingscode, moet de code binnen een try instructie worden geplaatst en een try blok worden genoemd. Toepassingscode die uitzonderingen verwerkt die door een try blok worden gegenereerd, wordt in een catch instructie geplaatst en wordt een catch blok genoemd. Nul of meer catch blokken zijn gekoppeld aan een try blok en elk catch blok bevat een typefilter waarmee de typen uitzonderingen worden bepaald die worden verwerkt.
Wanneer een uitzondering optreedt in een try blok, doorzoekt het systeem de bijbehorende catch blokken in de volgorde waarin ze worden weergegeven in de toepassingscode, totdat er een catch blok wordt gevonden dat de uitzondering verwerkt. Een catch blok verwerkt een uitzondering van het type T als het typefilter van het catch-blok aangeeft T of een type waarvan T is afgeleid. Het systeem stopt met zoeken nadat het het eerste catch blok heeft gevonden dat de uitzondering afhandelt. Daarom moet in toepassingscode een catch blok dat een type afhandelt, worden opgegeven voordat een catch blok dat de basistypen verwerkt, zoals wordt weergegeven in het voorbeeld dat volgt op deze sectie. Een catch-blok dat System.Exception verwerkt, wordt als laatste opgegeven.
Als geen van de catch blokken die zijn gekoppeld aan het huidige try blok de uitzondering afhandelt en het huidige try blok is genest binnen andere try blokken in de huidige aanroep, worden de catch blokken die zijn gekoppeld aan het volgende omsluitblok try doorzocht. Als er geen catch blok voor de uitzondering wordt gevonden, zoekt het systeem in de huidige aanroep naar eerdere nestniveaus. Als er geen catch blok voor de uitzondering wordt gevonden in de huidige aanroep, wordt de uitzondering doorgegeven aan de aanroepstack en wordt in het vorige stackframe gezocht naar een catch blok dat de uitzondering verwerkt. De doorzoeking van de aanroepstack wordt voortgezet totdat de uitzondering wordt afgehandeld of totdat er geen frames meer bestaan op de aanroepstack. Als de bovenkant van de aanroepstack wordt bereikt zonder een catch blok te vinden dat de uitzondering afhandelt, verwerkt de standaard-uitzonderingshandler deze en wordt de toepassing beëindigd.
F# try.. met expressie
F# maakt geen gebruik van catch blokken. In plaats daarvan wordt een opgeworpen uitzondering patroonmatig vergeleken via één with blok. Omdat dit een expressie is, in plaats van een instructie, moeten alle paden hetzelfde type retourneren. Zie The try voor meer informatie... met expressie.
Kenmerken van uitzonderingstype
Uitzonderingstypen ondersteunen de volgende functies:
Leesbare tekst die de fout beschrijft. Wanneer er een uitzondering optreedt, maakt de runtime een sms-bericht beschikbaar om de gebruiker te informeren over de aard van de fout en om actie voor te stellen om het probleem op te lossen. Dit tekstbericht wordt bewaard in de Message eigenschap van het uitzonderingsobject. Tijdens het maken van het uitzonderingsobject kunt u een tekenreeks doorgeven aan de constructor om de details van die specifieke uitzondering te beschrijven. Als er geen foutberichtargument wordt opgegeven aan de constructor, wordt het standaardfoutbericht gebruikt. Voor meer informatie, zie de eigenschap Message.
De status van de aanroepstack toen de uitzondering werd gegenereerd. De StackTrace eigenschap bevat een stack-trace die kan worden gebruikt om te bepalen waar de fout zich voordoet in de code. De stacktrace bevat alle aangeroepen methoden en de regelnummers in het bronbestand waar de aanroepen plaatsvinden.
Eigenschappen van uitzonderingsklasse
De Exception klasse bevat een aantal eigenschappen die helpen bij het identificeren van de codelocatie, het type, het Help-bestand en de reden voor de uitzondering: StackTrace, , InnerExceptionMessage, HelpLink, , HResult, Sourceen TargetSite.Data
Wanneer er een causale relatie bestaat tussen twee of meer uitzonderingen, behoudt de InnerException eigenschap deze informatie. De buitenste uitzondering wordt gegenereerd als reactie op deze interne uitzondering. De code die de buitenste uitzondering afhandelt, kan de informatie uit de eerdere interne uitzondering gebruiken om de fout beter af te handelen. Aanvullende informatie over de uitzondering kan worden opgeslagen als een verzameling sleutel-waardeparen in de Data eigenschap.
De tekenreeks voor foutberichten die tijdens het maken van het uitzonderingsobject wordt doorgegeven aan de constructor, moet worden gelokaliseerd en kan worden opgegeven vanuit een resourcebestand met behulp van de ResourceManager klasse. Zie de onderwerpen Over het maken van satellietassembly's en het verpakken en implementeren van resources voor meer informatie over gelokaliseerde resources.
Als u de gebruiker uitgebreide informatie wilt geven over de reden waarom de uitzondering is opgetreden, kan de HelpLink eigenschap een URL (of URN) bevatten naar een Help-bestand.
De Exception klasse maakt gebruik van HRESULT COR_E_EXCEPTION, met de waarde 0x80131500.
Zie de Exception constructors voor een lijst met initiële eigenschapswaarden voor een exemplaar van de Exception klasse.
Prestatieoverwegingen
Het genereren of verwerken van een uitzondering verbruikt een aanzienlijke hoeveelheid systeembronnen en uitvoeringstijd. Gooi uitzonderingen alleen om werkelijk buitengewone omstandigheden af te handelen, niet om voorspelbare gebeurtenissen of stroombeheer af te handelen. In sommige gevallen, bijvoorbeeld wanneer u een klassebibliotheek ontwikkelt, is het redelijk om een uitzondering te genereren als een methodeargument ongeldig is, omdat u verwacht dat uw methode wordt aangeroepen met geldige parameters. Een ongeldig methodeargument, als dit niet het resultaat is van een gebruiksfout, betekent dit dat er iets bijzonders is opgetreden. Gooi daarentegen geen uitzondering als gebruikersinvoer ongeldig is, omdat u kunt verwachten dat gebruikers af en toe ongeldige gegevens invoeren. Geef in plaats daarvan een mechanisme voor opnieuw proberen op, zodat gebruikers geldige invoer kunnen invoeren. U moet ook geen uitzonderingen gebruiken om gebruiksfouten af te handelen. Gebruik in plaats daarvan asserties om gebruiksfouten te identificeren en te corrigeren.
Gooi bovendien geen uitzondering wanneer een retourcode voldoende is; een retourcode niet converteren naar een uitzondering; en niet regelmatig een uitzondering vangen, negeren en vervolgens doorgaan met verwerken.
Een uitzondering hergooien
In veel gevallen wil een uitzonderingshandler de uitzondering gewoon doorgeven aan de beller. Dit gebeurt meestal in:
Een klassebibliotheek die op zijn beurt aanroepen naar methoden in de .NET-klassebibliotheek of andere klassenbibliotheken omhult.
Een toepassing of bibliotheek die een fatale uitzondering tegenkomt. De uitzonderingshandler kan de uitzondering registreren en vervolgens de uitzondering opnieuw genereren.
De aanbevolen manier om een uitzondering opnieuw te genereren, is door simpelweg de instructie throw te gebruiken in C#, de functie reraise in F# en de instructie Throw in Visual Basic zonder een expressie op te geven. Dit zorgt ervoor dat alle gespreksstackgegevens behouden blijven wanneer de uitzondering wordt doorgegeven aan de beller. In het volgende voorbeeld ziet u dit. Een tekenreeksextensiemethode verpakt één of meer aanroepen van FindOccurrences, zonder vooraf de argumenten te valideren.
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
Een beller belt FindOccurrences vervolgens twee keer. In de tweede aanroep naar FindOccurrencesgeeft de aanroeper een null door als de zoekreeks, waardoor de String.IndexOf(String, Int32) methode een ArgumentNullException uitzondering genereert. Deze uitzondering wordt verwerkt door de FindOccurrences methode en doorgegeven aan de aanroeper. Omdat de instructie throw zonder expressie wordt gebruikt, laat de uitvoer uit het voorbeeld zien dat de aanroepstack behouden blijft.
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 ({e.GetType().Name}) occurred.");
Console.WriteLine($"Message:{Environment.NewLine} {e.Message}{Environment.NewLine}");
Console.WriteLine($"Stack Trace:{Environment.NewLine} {e.StackTrace}{Environment.NewLine}");
}
}
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()
Als de uitzondering daarentegen opnieuw wordt opgegooid met behulp van deze uitdrukking:
throw e;
Throw e
raise e
... dan blijft de volledige aanroepstack niet behouden en genereert het voorbeeld de volgende uitvoer:
'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()
Een iets lastiger alternatief is om een nieuwe uitzondering te genereren en de aanroepstackgegevens van de oorspronkelijke uitzondering in een interne uitzondering te behouden. De aanroeper kan vervolgens de InnerException-eigenschap van de nieuwe uitzondering gebruiken om het stackframe en andere informatie over de oorspronkelijke uitzondering op te halen. In dit geval is de throw-instructie:
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)
De gebruikerscode die de uitzondering afhandelt, moet weten dat de InnerException eigenschap informatie bevat over de oorspronkelijke uitzondering, zoals de volgende uitzonderingshandler illustreert.
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine($"An exception ({e.GetType().Name}) occurred.");
Console.WriteLine($" Message:{Environment.NewLine}{e.Message}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {e.StackTrace}");
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine($" Exception Name: {ie.GetType().Name}");
Console.WriteLine($" Message: {ie.Message}{Environment.NewLine}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {ie.StackTrace}{Environment.NewLine}");
}
}
// 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)
Standaard uitzonderingen kiezen
Wanneer u een uitzondering moet genereren, kunt u vaak een bestaand uitzonderingstype in .NET gebruiken in plaats van een aangepaste uitzondering te implementeren. Gebruik een standaard uitzonderingstype onder deze twee voorwaarden:
U genereert een uitzondering die wordt veroorzaakt door een gebruiksfout (dat wil gezegd door een fout in programmalogica die is gemaakt door de ontwikkelaar die uw methode aanroept). Normaal gesproken genereert u een uitzondering zoals ArgumentException, ArgumentNullException, InvalidOperationExceptionof NotSupportedException. De tekenreeks die u opgeeft aan de constructor van het uitzonderingsobject bij het instantiëren van het uitzonderingsobject, moet de fout beschrijven, zodat de ontwikkelaar dit kan oplossen. Voor meer informatie, zie de eigenschap Message.
U verwerkt een fout die kan worden gecommuniceerd met de beller met een bestaande .NET-uitzondering. U moet de hoogst afgeleide uitzondering kunnen genereren. Als een methode bijvoorbeeld vereist dat een argument een geldig lid is van een opsommingstype, moet u een InvalidEnumArgumentException (de meest afgeleide klasse) gooien in plaats van een ArgumentException.
De volgende tabel bevat algemene uitzonderingstypen en de voorwaarden waaronder u ze zou gooien.
| Uitzondering | Conditie |
|---|---|
| ArgumentException | Een niet-null-argument dat wordt doorgegeven aan een methode, is ongeldig. |
| ArgumentNullException | Een argument dat wordt doorgegeven aan een methode is null. |
| ArgumentOutOfRangeException | Een argument valt buiten het bereik van geldige waarden. |
| DirectoryNotFoundException | Een deel van een mappad is ongeldig. |
| DivideByZeroException | De noemer in een geheel getal of Decimal deelbewerking is nul. |
| DriveNotFoundException | Een schijf is niet beschikbaar of bestaat niet. |
| FileNotFoundException | Er bestaat geen bestand. |
| FormatException | Een waarde heeft geen geschikte indeling om te worden omgezet vanuit een tekenreeks door een conversiemethode zoals Parse. |
| IndexOutOfRangeException | Een index valt buiten de grenzen van een matrix of verzameling. |
| InvalidOperationException | Een methodeaanroep is ongeldig in de huidige status van een object. |
| KeyNotFoundException | De opgegeven sleutel voor toegang tot een lid in een verzameling kan niet worden gevonden. |
| NotImplementedException | Er is geen methode of bewerking geïmplementeerd. |
| NotSupportedException | Een methode of bewerking wordt niet ondersteund. |
| ObjectDisposedException | Er wordt een bewerking uitgevoerd op een object dat is verwijderd. |
| OverflowException | Een rekenkundige bewerking, cast- of conversiebewerking resulteert in een overloop. |
| PathTooLongException | Een pad of bestandsnaam overschrijdt de maximale door het systeem gedefinieerde lengte. |
| PlatformNotSupportedException | De bewerking wordt niet ondersteund op het huidige platform. |
| RankException | Een matrix met het verkeerde aantal dimensies wordt doorgegeven aan een methode. |
| TimeoutException | Het tijdsinterval dat aan een bewerking is toegewezen, is verlopen. |
| UriFormatException | Er wordt een ongeldige URI (Uniform Resource Identifier) gebruikt. |
Aangepaste uitzonderingen implementeren
In de volgende gevallen is het gebruik van een bestaande .NET-uitzondering voor het afhandelen van een foutvoorwaarde niet voldoende:
Wanneer de uitzondering een unieke programmafout weerspiegelt die niet kan worden toegewezen aan een bestaande .NET-uitzondering.
Wanneer voor de uitzondering een andere behandeling vereist is dan de afhandeling die geschikt is voor een bestaande .NET-uitzondering, of als de uitzondering moet worden onderscheiden van een vergelijkbare uitzondering. Als u bijvoorbeeld een ArgumentOutOfRangeException uitzondering genereert bij het parseren van de numerieke weergave van een tekenreeks die buiten het bereik van het doelintegraal type valt, wilt u niet dezelfde uitzondering gebruiken voor een fout die het gevolg is van de aanroeper die niet de juiste beperkte waarden opgeeft bij het aanroepen van de methode.
De Exception klasse is de basisklasse van alle uitzonderingen in .NET. Veel afgeleide klassen zijn afhankelijk van het overgeërfde gedrag van de elementen van de Exception-klasse; ze overschrijven de elementen van Exception niet, noch definiëren ze unieke elementen.
Uw eigen uitzonderingsklasse definiëren:
Definieer een klasse die overkomt van Exception. Definieer indien nodig unieke leden die uw klas nodig heeft om aanvullende informatie over de uitzondering te verstrekken. De ArgumentException klasse bevat bijvoorbeeld een ParamName eigenschap waarmee de naam van de parameter wordt opgegeven waarvan het argument de uitzondering heeft veroorzaakt en de RegexMatchTimeoutException eigenschap een MatchTimeout eigenschap bevat die het time-outinterval aangeeft.
Overschrijf indien nodig de overgeërfde leden waarvan u de functionaliteit wilt aanpassen. Houd er rekening mee dat de meeste bestaande afgeleide klassen Exception het gedrag van overgenomen leden niet overschrijven.
Bepaal of uw aangepaste uitzonderingsobject serialiseerbaar is. Met serialisatie kunt u informatie over de uitzondering opslaan en kan uitzonderingsgegevens worden gedeeld door een server en een clientproxy in een externe context. Als u het uitzonderingsobject serialiseerbaar wilt maken, markeert u het met het SerializableAttribute kenmerk.
Definieer de constructors van uw uitzonderingsklasse. Uitzonderingsklassen hebben doorgaans een of meer van de volgende constructors:
Exception(), waarbij standaardwaarden worden gebruikt om de eigenschappen van een nieuw uitzonderingsobject te initialiseren.
Exception(String), waarmee een nieuw uitzonderingsobject wordt geïnitialiseerd met een opgegeven foutbericht.
Exception(String, Exception), waarmee een nieuw uitzonderingsobject wordt geïnitialiseerd met een opgegeven foutbericht en interne uitzondering.
Exception(SerializationInfo, StreamingContext), een
protectedconstructor waarmee een nieuw uitzonderingsobject van geserialiseerde gegevens wordt geïnitialiseerd. U moet deze constructor implementeren als u ervoor hebt gekozen om uw uitzonderingsobject serializeerbaar te maken.
In het volgende voorbeeld ziet u het gebruik van een aangepaste uitzonderingsklasse. Er wordt een NotPrimeException uitzondering gedefinieerd die wordt gegenereerd wanneer een client probeert een reeks priemgetallen op te halen door een beginnummer op te geven dat geen priemgetal is. De uitzondering definieert een nieuwe eigenschap, NonPrimedie het niet-priemgetal retourneert dat de uitzondering heeft veroorzaakt. Naast het implementeren van een beveiligde parameterloze constructor en een constructor met SerializationInfo en StreamingContext parameters voor serialisatie, definieert de NotPrimeException klasse drie extra constructors ter ondersteuning van de NonPrime eigenschap. Elke constructor roept een basisklasseconstructor aan en bewaart tegelijkertijd de waarde van het niet-priemgetal. De NotPrimeException klasse wordt ook gemarkeerd met het SerializableAttribute kenmerk.
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
In PrimeNumberGenerator de klasse die in het volgende voorbeeld wordt weergegeven, wordt de zeef van Eratosthenes gebruikt om de reeks priemgetallen van 2 te berekenen tot een limiet die is opgegeven door de client in de aanroep van de klasseconstructor. De GetPrimesFrom methode retourneert alle priemgetallen die groter zijn dan of gelijk zijn aan een opgegeven ondergrens, maar genereert een NotPrimeException als die ondergrens geen priemnummer is.
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
In het volgende voorbeeld worden twee aanroepen uitgevoerd naar de GetPrimesFrom methode met niet-priemgetallen, waarvan één de grenzen van het toepassingsdomein overschrijdt. In beide gevallen wordt de uitzondering gegenereerd en succesvol verwerkt in de clientcode.
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 {start} prime numbers from {limit} to {2}");
}
catch (NotPrimeException e)
{
Console.WriteLine($"{e.NonPrime} is not prime");
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($"{e.NonPrime} is not prime");
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()
' --------
Voorbeelden
In het volgende voorbeeld ziet u een catch (with in F#) blok dat is gedefinieerd voor het afhandelen ArithmeticException van fouten. Dit catch blok onderschept ook DivideByZeroException fouten, omdat DivideByZeroException van ArithmeticException afstamt en er geen catch blok expliciet is gedefinieerd voor DivideByZeroException fouten.
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()
'