Fortgeschrittene Grundlagen: Fehlerbehandlung

Die Fehlerbehandlung dient zum Abfangen von Exceptions. Solche Exceptions können von aufgerufenen Funktionen ausgelöst werden. Ein häufiger Einsatz solcher Exceptions (oder auch Ausnahmen genannt) findet sich bei Zugriffen auf Dateien und Streams (dazu später mehr). Wenn solche Exceptions im Programm nicht abgefangen werden, stürzt das Programm u. U. komplett ab. Deshalb wollen wir uns in diesem Thema damit beschäftigen, wie wir solche Exceptions abfangen können.
Grundlegend gilt zu sagen, dass alle Exceptions der Basisklasse Exception (Namensraum System) angehören. Für verschiedene Anwendungseinsätze werden jedoch von Exception abgeleitete Klassen verwendet (z. B. FormatException, OverflowException oder NullReferenceException). Der Programmcode, welcher eine Exception auslösen kann, sollte daher mit dem try-Block umschlossen werden. Darin können natürlich auch Programmzeilen notiert werden, welche keine Exception auslösen können. Um nun den Fehlerfall zu behandeln, benötigen wir den catch-Block. Der Kopf des catch-Blocks bildet sich aus dem Schlüsselwort catch und runden Klammern, in welcher wir eine Variable deklarieren (z. B. Exception ex). Dabei entspricht der Datentyp der Variable der Exception-Klasse. Der Variablenname kann wie immer frei gewählt werden. Die Basisklasse Exception kann für alle Exceptions verwendet werden und ist vor allem dann hilfreich, wenn es egal ist, welche exakte Form der Exception ausgelöst wurde. Der Code des catch-Blocks wird also ausgeführt, sobald eine Exception im try-Block auftritt. Der restliche Code des try-Blocks (wenn vorhanden) wird nicht mehr ausgeführt. catch-Blöcke können untereinander gestapelt werden. Hierbei ist die Reihenfolge entscheidend: Wird im ersten catch-Block der Typ Exception abgefangen und im zweiten Block der Typ NullReferenceException, so wird das Programm niemals im zweiten Block landen, da die NullReferenceException bereits durch den Typ Exception abgefangen wird.
Bei Stream-Zugriffen ist es des Weiteren oftmals wichtig, den Stream zu schließen, unabhängig davon, ob eine Exception aufgetreten ist oder nicht. Dafür gibt es den finally-Block. Der finally-Block wird unterhalb der catch-Blöcke notiert. Der Code innerhalb des finally-Blocks wird ausgeführt, nachdem der Code im try-Block oder der des catch-Blocks (falls eine Exception ausgelöst wurde) ausgeführt wurde.
Exceptions sind keine Fehler, welche vom Prozessor ausgelöst werden. Im Gegenteil, es sind Fehler, die im .NET-Framework abgefangen sind. Genau aus diesem Grund ist es auch möglich, solche Exceptions im C#-Programmcode als Programmierer selbst auszulösen. Hierfür benötigen wir ein Objekt der gewünschten Exception-Klasse. Über das Schlüsselwort throw können wir dann eine Exception auslösen. Hinter dem Schlüsselwort throw müssen wir dann das erstellte Objekt notieren. Aufgrund der Bezeichnung des Schlüsselworts wird auch oft davon gesprochen, dass eine Exception „geworfen“ (engl. throw) wird. Natürlich können Sie auch eigene Exceptions erstellen, indem Sie eine Klasse erstellen, welche die Klasse Exception als Basisklasse verwendet. Den Exception-Klassen können im Konstruktor standardmäßig die Fehlermitteilung übergeben werden (Eigenschaft Message). Mit der ToString()-Methode kann des Weiteren eine detaillierte Fehlermeldung angezeigt werden. Hierfür sollten jedoch noch weitere Eigenschaften gesetzt werden.

Program.cs

int iZahl;

try
{
	Console.Write("Bitte geben Sie eine Zahl ein: ");
	// diese Funktion kann eine FormatException oder eine OverflowException auslösen
	iZahl = Convert.ToInt32(Console.ReadLine());
}
catch (FormatException fex)
{
	Console.WriteLine("Ein Formatierungs-Fehler ist aufgetreten:");
	Console.WriteLine(fex.ToString());
}
catch (OverflowException oex)
{
	Console.WriteLine("Ein Überlauf-Fehler ist aufgetreten:");
	Console.WriteLine(oex.ToString());
}
finally
{
	Console.WriteLine("Falls wir Stream-Zugriffe o. Ä. im try-Block vornehmen können wir");
	Console.WriteLine("hier unabhängig ob der Vorgang erfolgreich war oder nicht,");
	Console.WriteLine("Ressourcen wieder freigeben (z. B. Streams schließen)");
}

Console.WriteLine();

try
{
	Console.WriteLine("Wir lösen gleich einen Fehler aus!");
	// das Auslösen von Exception's wird hauptsächlich in größeren Projekten verwendet,
	// kann jedoch in einigen Fällen sehr hilfreich sein
	throw new Exception("Ein Fehler wurde mit Absicht ausgelöst!");
}
// Exception ist eine Basisklasse und kann daher für eine alle Fehlerbehandlungen verwendet werden
catch (Exception ex)
{
	Console.WriteLine("Ein Fehler ist aufgetreten!");
	Console.WriteLine("Die übergebene Fehler-Meldung ist: " + ex.Message);
}

Console.ReadKey();
Download

LinksRechts