Delen via


Uitzonderingen gebruiken

In C# worden fouten in het programma tijdens runtime doorgegeven via het programma met behulp van een mechanisme dat uitzonderingen wordt genoemd. Uitzonderingen worden gegenereerd door code die een fout tegenkomt en wordt gedetecteerd door code die de fout kan corrigeren. Uitzonderingen kunnen worden gegenereerd door de .NET-runtime of door code in een programma. Zodra er een uitzondering wordt opgegooid, verspreidt deze zich naar boven in de aanroepstack totdat er een catch instructie voor de uitzondering wordt gevonden. Niet-onderschepte uitzonderingen worden verwerkt door een algemene uitzonderingshandler, door het systeem geleverd, die een dialoogbox weergeeft.

Uitzonderingen worden vertegenwoordigd door klassen die zijn afgeleid van Exception. Deze klasse identificeert het type uitzondering en bevat eigenschappen met details over de uitzondering. Het genereren van een uitzondering omvat het maken van een exemplaar van een uitzonderingsklasse, eventueel het configureren van eigenschappen van de uitzondering en het genereren van het object met behulp van het throw trefwoord. Voorbeeld:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Nadat er een uitzondering is opgetreden, controleert de runtime de huidige instructie om te zien of deze zich in een try blok bevindt. Als dat het geval is, worden alle catch blokken die aan het try blok zijn gekoppeld, gecontroleerd om te zien of ze de uitzondering kunnen ondervangen. Catch blokken geven doorgaans uitzonderingstypen op; als het type van het catch blok hetzelfde type is als de uitzondering of een basisklasse van de uitzondering, kan het catch blok de methode verwerken. Voorbeeld:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Als de instructie waarmee een uitzondering wordt gegenereerd zich niet binnen een try blok bevindt of als het try blok dat het insluit geen overeenkomend catch blok bevat, controleert de runtime de aanroepmethode voor een try instructie en catch blokken. De runtime vervolgt de aanroepstack en zoekt naar een compatibel catch blok. Nadat het catch blok is gevonden en uitgevoerd, wordt de controle doorgegeven aan de volgende instructie na dat catch blok.

Een try instructie kan meer dan één catch blok bevatten. De eerste catch instructie die de uitzondering kan verwerken, wordt uitgevoerd. Alle volgende catch instructies, zelfs als ze compatibel zijn, worden genegeerd. Orden vangblokken van de meest specifieke (of meest afgeleide) naar de minst specifieke. Voorbeeld:

using System;
using System.IO;

namespace Exceptions
{
    public class CatchOrder
    {
        public static void Main()
        {
            try
            {
                using (var sw = new StreamWriter("./test.txt"))
                {
                    sw.WriteLine("Hello");
                }
            }
            // Put the more specific exceptions first.
            catch (DirectoryNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            // Put the least specific exception last.
            catch (IOException ex)
            {
                Console.WriteLine(ex);
            }
            Console.WriteLine("Done");
        }
    }
}

Voordat het catch blok wordt uitgevoerd, controleert de runtime op finally blokken. Finally blokken stellen de programmeur in staat om elke ambiguë toestand op te ruimen die van een afgebroken try blok kan worden achtergelaten, of om externe resources (zoals grafische handles, databaseverbindingen of bestandsstromen) vrij te maken zonder te wachten tot de garbagecollector in de runtime de objecten heeft voltooid. Voorbeeld:

static void TestFinally()
{
    FileStream? file = null;
    //Change the path to something that works on your machine.
    FileInfo fileInfo = new System.IO.FileInfo("./file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        file?.Close();
    }

    try
    {
        file = fileInfo.OpenWrite();
        Console.WriteLine("OpenWrite() succeeded");
    }
    catch (IOException)
    {
        Console.WriteLine("OpenWrite() failed");
    }
}

Als WriteByte() een uitzondering werd opgeworpen, zou de code in het tweede try blok dat probeert het bestand opnieuw te openen, mislukken als file.Close() niet wordt aangeroepen, en zou het bestand vergrendeld blijven. Omdat finally blokken worden uitgevoerd, zelfs als er een uitzondering wordt gegenereerd, kan het finally bestand in het vorige voorbeeld correct worden gesloten en wordt een fout voorkomen.

Als er geen compatibel catch blok op de aanroepstack wordt gevonden nadat er een uitzondering is opgetreden, treedt er een van de volgende drie dingen op:

  • Als de uitzondering zich binnen een finalizer bevindt, wordt de finalizer afgebroken en wordt de basis finalizer, indien aanwezig, aangeroepen.
  • Als de aanroepstack een statische constructor of een initialisatiefunctie voor statische velden bevat, wordt er een TypeInitializationException gegenereerd met de oorspronkelijke uitzondering die is toegewezen aan de InnerException eigenschap van de nieuwe uitzondering.
  • Als het begin van de thread is bereikt, wordt de thread beëindigd.