Delen via


System.Exception-klasse

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 obj argument 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 Module
    

    De NullReferenceException uitzondering die het resultaat is wanneer objnull kan 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 een null argument 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: 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
    

    In 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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 protected constructor 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()
'