OOD Handboek

8Exceptions

#Standaard exceptions: welke wanneer

ExceptionGebruik bij
ArgumentExceptionongeldig argument (leeg id, einddatum vóór begindatum)
ArgumentNullExceptionargument is null
ArgumentOutOfRangeExceptiongetal buiten bereik (accu 0..100, negatieve leeftijd)
InvalidOperationExceptionactie past niet bij de toestand (auto starten die al rijdt)
KeyNotFoundExceptionsleutel niet in dictionary
NullReferenceException(gooi je zelf nooit) iets was null
FileNotFoundExceptionbestand ontbreekt
FormatExceptiontekst is geen geldig getal (double.Parse)
Eigen exceptiondomeinregel geschonden (voertuig niet beschikbaar, saldo onvoldoende)

#Gooien (throw)

public Student(string naam, int leeftijd)
{
    ArgumentException.ThrowIfNullOrWhiteSpace(naam);
    if (leeftijd < 16 || leeftijd > 120)
        throw new ArgumentOutOfRangeException(nameof(leeftijd), leeftijd,
            "Leeftijd moet tussen 16 en 120 liggen.");
    Naam = naam;
    Leeftijd = leeftijd;
}
Uitleg

Controleer argumenten aan het begin van een constructor of methode en gooi meteen een passende exception. Zo kan een object nooit in een ongeldige toestand bestaan. nameof zet de parameternaam in de foutmelding zonder dat je hem als tekst hoeft te typen.

Functies en keywords
nameof
Geeft de naam van een variabele of parameter als tekst, en blijft kloppen als je hem hernoemt.
throw
Gooit een exception; de methode stopt direct en de fout gaat omhoog naar de aanroeper.
ArgumentOutOfRangeException
Voor een waarde buiten het toegestane bereik.
ThrowIfNullOrWhiteSpace
Gooit een exception als de tekst null, leeg of alleen spaties is (.NET 8+).

#Custom exception (sjabloon)

public class VoertuigNietBeschikbaarException : Exception
{
    public string VoertuigId { get; }

    public VoertuigNietBeschikbaarException(string voertuigId, string melding)
        : base(melding)                       // melding → ex.Message
    {
        VoertuigId = voertuigId;
    }

    // optioneel: met inner exception
    public VoertuigNietBeschikbaarException(string melding, Exception inner)
        : base(melding, inner) { VoertuigId = ""; }
}
Uitleg

Een eigen exception erft van Exception en geeft de melding door met : base(melding), zodat ex.Message werkt. Extra properties (zoals het voertuig-id) geven de aanroeper context. De tweede constructor bewaart een onderliggende fout als inner exception.

Functies en keywords
base(...)
Roept de constructor van de ouderklasse aan, vóór de eigen constructor draait.
: Exception
Eigen exception-klasse; erft alles van de standaard Exception.
{ get; }
Alleen-lezen property; de waarde wordt in de constructor gezet.

Hiërarchie: BigBrutusException : Exception, en ApparaatOfflineException : BigBrutusException enz. Dan kun je met één catch (BigBrutusException) alles van je domein vangen.

#Vangen: try / catch / finally

StreamReader? reader = null;
try
{
    reader = new StreamReader("cijfers.txt");
    string regel = reader.ReadLine()!;
    Console.WriteLine($"Eerste cijfer: {double.Parse(regel)}");
}
catch (FileNotFoundException ex)      // specifiek EERST
{
    Console.WriteLine($"Niet gevonden: {ex.FileName}");
}
catch (FormatException)               // variabele mag weg
{
    Console.WriteLine("Geen geldig cijfer.");
}
catch (Exception ex)                  // algemeen als LAATSTE
{
    Console.WriteLine($"Onverwacht: {ex.Message}");
}
finally                                // draait ALTIJD
{
    reader?.Dispose();
}
Uitleg

try bevat de code die mis kan gaan, de catch-blokken vangen specifieke fouten af (specifiek eerst, algemeen als laatste), en finally draait altijd, ook na een fout. Hier wordt het bestand daar netjes gesloten.

Functies en keywords
?.
Roept alleen aan als het object niet null is; anders is het resultaat null.
Console.ReadLine
Leest een regel invoer als tekst (kan null zijn).
try / catch
try bevat code die kan falen; catch vangt een specifiek type fout af.
finally
Blok dat altijd draait, met of zonder fout; voor opruimen.
Dispose
Geeft een resource (zoals een open bestand) vrij.
StreamReader
Leest een tekstbestand regel voor regel.
$"...{x}"
String-interpolatie: zet de waarde van x direct in de tekst.
// using: automatisch Dispose, vervangt finally
using var reader = new StreamReader(pad);

// when: filter
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)

// opnieuw gooien met behoud van stacktrace
catch (Exception ex) { _log.Fout(ex.Message); throw; }  // niet: throw ex;
Uitleg

using var sluit een resource automatisch aan het einde van het blok, dus geen finally nodig. Met when vang je een exception alleen als een extra voorwaarde klopt. Gebruik throw; zonder variabele om opnieuw te gooien met behoud van de originele stacktrace.

Functies en keywords
throw
Gooit een exception; de methode stopt direct en de fout gaat omhoog naar de aanroeper.
when
Extra voorwaarde bij een catch: alleen vangen als die klopt.
using var
Ruimt de resource automatisch op (Dispose) aan het einde van het blok.
StreamReader
Leest een tekstbestand regel voor regel.
var
De compiler leidt het type zelf af uit de rechterkant.

#Wanneer vangen?

  • Vang alleen als je op die plek iets zinnigs kunt doen: herstellen, context toevoegen (opnieuw gooien), of op de buitenste laag (Program/UI) netjes melden.
  • Niet vangen om te negeren: een lege catch { } is een swallowed exception (anti-pattern).
  • Verwachte gebruikersfouten (geen getal getypt) los je op zonder exception: int.TryParse.
  • Log vóór je gooit als een weigering zichtbaar moet zijn in de log, en geef dan de exception door.

#Ontwerpkeuzes

SituatieAanpak
Negatieve leeftijd in constructorArgumentOutOfRangeException gooien (object mag niet ongeldig bestaan)
Externe betaal-API faalt (HttpRequestException)Niet verbergen: op buitenste laag vangen, loggen (Error), evt. retry, nette melding
Product niet op voorraad (vraag 10, nog 3)Eigen domein-exception (bv. OnvoldoendeVoorraadException) met de getallen als properties
Einddatum vóór begindatumArgumentException in de constructor
Auto starten die al rijdtInvalidOperationException (verkeerde toestand)
Gebruiker typt "twintig" i.p.v. een getalGeen exception: int.TryParse en opnieuw vragen

Stacktrace lezen: bovenste regel = type + melding. Daaronder at Klasse.Methode() in Bestand.cs:line N: de bovenste regel uit jouw code is meestal de plek van de fout.