Kolumne: C# im Fokus

Fehlerbehandlung mit Struktur
Kommentare

Eine durchgängige und korrekte Fehlerbehandlung ist für jedes Programm wichtig, um eine gewisse Stabilität zu erreichen. Dabei sollten auch Fehler zielgerichtet behoben und nicht nur einfach abgefangen werden. Welche Möglichkeiten unter C# zur Verfügung stehen, ist Inhalt der heutigen C#-Kolumne.

Der Mechanismus der Fehlererkennung und Behandlung hat sich während der Entwicklung von Programmiersprachen verändert. Zu Beginn wurden hauptsächlich über Rückgabewerte Fehlercodes zurückgeliefert, die über Erfolg beziehungsweise Misserfolg des Methoden- oder Funktionsaufrufs informierten. Dieses Vorgehen, das auch teilweise heute noch verwendet wird, hat zwei wesentliche Nachteile. Zum einen muss zwingend immer ein Wert zurückgegeben werden. Das erschwert es, zwischen Methoden und Funktionen zu unterscheiden. Bei Funktionen wird es zudem noch komplizierter, da neben dem eigentlichen berechneten Funktionswert noch ein Fehler- beziehungsweise Statuscode zurückgegeben werden muss. Zum anderen gibt es keinen Zwang, den Fehler zu behandeln. Wird eine Methode aufgerufen und der Fehlercode nach dem Methodenaufruf nicht explizit ausgewertet, geht der Fehler verloren. Das kann zu einem instabilen System und somit zu Folgefehlern führen.

Erzwungene Fehlerbehandlung

Das .NET Framework besitzt einen sehr viel effizienteren Mechanismus, der unbehandelte Fehler nicht zulässt. Hierbei handelt es sich um das bekannte try-ctach-finally-Konstrukt. Die Arbeitsweise des Mechanismus ist einfach erklärt: Eine Funktion oder Methode, die potenziell einen Fehler hervorrufen kann, wird innerhalb eines try-catch-Blocks eingeschlossen. Kommt es bei der Ausführung der gefährdeten Methode zu einem Fehler beziehungsweise zu einer Ausnahme, wird sie von dem catch-Block gefangen und behandelt. Fehler sollten dabei, wenn möglich, an Ort und Stelle behandelt werden. Ist das nicht möglich, kann die Ausnahme an den Aufrufer weitergegeben werden. Wird selbst keine Fehlerbehandlung durchgeführt, greift in letzter Instanz die Ausnahmebehandlungsroutine der .NET-Laufzeitumgebung. Sie endet je nach Konfiguration und Ausführungsumgebung in der Anzeige der Fehlermeldung in einem Meldungsfenster. Listing 1 zeigt zu der zuvor beschriebenen Vorgehensweise ein klassisches Beispiel. Die Methode Calculate dividiert den festen Wert 100 durch einen übergebenen Wert. Wird der Methode eine 0 übergeben, endet das in einer klassischen DivideByZeroException-Ausnahme. Daher wird der Aufruf in einem try-ctach-Block ausgeführt. Bei einem Fehler wird der zugehörige catch-Block aufgerufen.

Ausnahmehierarchie

Listing 1 zeigt darüber hinaus die Möglichkeit, wie auf verschiedene Ausnahmen (Exceptions) speziell reagiert werden kann. Bei der Ausführung der Methode Calculate können zwei Ausnahmen auftreten: Zum einen kann es zu einer Division durch 0 kommen (DivideByZeroException), und zum anderen kann ein definierter Wert unterschritten werden (ArgumentOutOfRangeException). Damit der Aufrufer der Methode gezielt auf mögliche Fehler reagieren kann, können mehrere catch-Blöcke notiert werden. Je nach Ausnahme wird der erste passende catch-Block aufgerufen. Die verbleibenden catch-Blöcke werden nicht aufgerufen. Da der erste passende catch-Block verwendet wird, müssen die Ausnahmen in einer bestimmen Reihenfolge definiert werden. Hierbei gilt es, die speziellen vor den allgemeinen Ausnahmen zu notieren. Würde beispielsweise beim Beispiel in Listing 1 als Erstes ein catch-Block aufgeführt, der die allgemeine Ausnahme Exception behandeln würde, würden die folgenden catch-Blöcke niemals aufgerufen. Eine solche Konstellation wird auch bereits vom Compiler erkannt und mit der Meldung „A previous catch clause already catches all exceptions of this or of a super type (System.Exception)“ dem Entwickler mitgeteilt.

Listing 1

public static void Calculate(int divBy)
{
  int result = 100 / divBy;
  if (result < 10)
    throw new ArgumentOutOfRangeException("divBy");
}
static void Main(string[] args)
{
  try
  {
    Calculate (0);
  }
  catch (DivideByZeroException excObject)
  {
    Console.WriteLine("Fehler: {0}", excObject.Message);
  }
  catch (ArgumentOutOfRangeException excObject)
  {
    Console.WriteLine("Fehler: {0}", excObject.Message);
  }
}  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -