Umgang mit Ausnahmen

Herzlich willkommen zum vorerst letzten Teil unseres Crashkurses für Neulinge. Wir behandeln nun noch Exceptions, damit ihr auch wisst, wie Programmfehler zur Laufzeit abgefangen werden können. Danach solltet ihr, mit etwas Übung und Fleiß, in der Lage sein, grundlegende Aspekte von C# zu verstehen. Als da wären Objektorientierung (wer das versteht, hat schon mal die halbe Miete), Objekte Instanziieren, Klassenmodell, Methoden aufrufen und überladen… und nun auch Fehler behandeln. Zunächst: Wann tritt ein Fehler auf? Fehler können aus den unterschiedlichsten Gründen auftreten. Wurde ein Timeout überschritten, eine Ressource nicht gefunden, liegt ein Fehler in der Programmierung vor (die vorallem auftreten wenn nicht geprüft wurde ob Objekte nicht null sind), stackoverflows… etc. Damit nicht jeder Fehler sofort zum zusammenbrechen unserer gesamten Anwendung führt, können wir die Fehler abfangen. Die Operation die gerade ausgeführt wird muss zwar trotzdem erst einmal abgebrochen werden, jedoch können wir nun auf den Fehler reagieren und noch so viel wie möglich retten.

Wir schreiben mal ein kleines Programm, in dem wir einen Fehler provozieren.

static public void ProvokeException()
{
 FileStream fs = new FileStream(@"X:\blubb.bla", FileMode.Open, FileAccess.Read);
 long len = fs.Length;
}

Wir referenzieren eine Datei, die gar nicht existiert. Prompt wirft die Methode einen Fehler, und unser Programm stürzt ab. Jetzt fangen wir den Fehler jedoch ab, und können so schon einmal im debugger sehen, was da passiert. Dazu kommt unser Fehleranfälliger Code in einen try{} Block. Nach einem Try muss auf jeden fall ein catch oder finally folgen. Die Blöcke dürften eigentlich selbsterklärend sein, catch stellt Code dar, der ausgeführt wird, wenn ein Fehler aufgefangen wurde, finally beinhaltet code, der auf jeden fall, egal ob es eine Ausnahme gab oder nocht, ausgeführt wird.

try
{
 FileStream fs = new FileStream(@"X:\blubb.bla", FileMode.Open, FileAccess.Read);
 long len = fs.Length;
}
catch (DirectoryNotFoundException ex)
{
}

Wenn wir das Programm im Debugger ausführen, wird nichts passieren. Der Fehler wird abgefangen, da der catch-block leer ist, passiert nichts. Wir können festlegen, welche Fehler wir abfangen wollen. Exceptionfängt alle Ausnahmen ab, in unserem Fall wissen wir, es wird eine DirectoryNotFoundException ausgelöst, also fangen wir nur diese ab. Eine möglichkeit für den catch-block wäre es, die Fehlermeldung auszugeben. Als Text in einer MessageBox oder in einem Log:

catch (DirectoryNotFoundException ex)
{
 StreamWriter write = new StreamWriter("log.txt", true);
 write.WriteLine(ex.Message);
 write.Flush();
 write.Close();
}

Nun stellt dieser Code einen sehr einfachen Sachverhalt dar. Was ist aber, wenn wir nun Klassen und mehrere Objekte haben? Wenn wir zum beispiel eine Mathe-Klasse bereitstellen, die eine Divisionsmethode hat? Divisor und Dividend werden als Parameter übergeben. Wenn der Dividend 0 ist, wissen wir (hoffentlich) alle, das da etwas nicht stimmt. Anstatt unser armes Programm sturr durch 0 teilen zu lassen und eine DivideByZeroException auszulösen, teilen wir dem Aufrufer unserer Methode lieber mit, dass eine Ausnahme vorliegt. Wir könnten auch einfach die DivideByZeroException weiterleiten, den Code dazu stelle ich der Vollständigkeit halber bereit, jedoch bitte ich der Mathematik zu liebe niemanden, den Code zu kompilieren. Computer empfinden zwar nichts, aber in mir stirbt jedes mal ein kleiner Teil, wenn diese armen geschöpfe so missbraucht werden. Das Weiterleiten von Exceptions könnt ihr auch mit FileNotFoundExceptions üben.

//Program.cs
double s = 0;
try
{
 s = Except.Divide(5,0);
}
catch (DivideByZeroException ex)
{
 Console.WriteLine(ex.Message);
}
Console.WriteLine(s);
//Except.cs
public static double Divide(int dividend, int divisor)
{
double solved = 0;
if (divisor != 0)
solved = dividend / divisor;
else
throw new DivideByZeroException("This is a custom exception");

return solved;
}

Die Funktion Divide ist ganz einfach: Erkennen wir, dass ein Fehler vorliegt, lösen wir selbst eine neue Ausnahme aus. Die Methode, welche unsere Divide-Methode aufgerufen hat, muss sich nun um die Ausnahme kümmern. So würde es gehen, eine Ausnahme unverändert weiterzuleiten:

public static double Divide(int dividend, int divisor)
{
 double solved = 0;
 try
 {
  solved = dividend / divisor;
 }
 catch (DivideByZeroException ex)
 {
  throw ex;
 }
return solved;
}

Hier wird einfach direkt die von uns aufgefangene Ausnahme weitergereicht. Unser Code in der Hauptklasse bleibt unverändert. Und das war es auch schon. So können wir einen Großteil der Ausnahmen von Fehleranfälligen Methoden abfangen, und unser Programm stürzt nicht sofort ab. Wenn wir geschickt programmieren, können wir sogar versuchen, die Operation doch noch zu retten, indem wir die Daten aus unserer Ausnahme analysieren und entsprechend handeln. Hier ist wie immer der Download zum Projekt. Ich wünsche frohes schaffen, ab nun werde ich einige Programme von mir posten und analysieren. MfG NoMad

Dieser Eintrag wurde veröffentlicht in C#
Bookmarken: Permanent-Link Schreibe einen Kommentar oder hinterlasse einen Trackback: Trackback-URL.
Achtung: Wordpress interpretiert bestimmte Zeichenfolgen als Markup und verändert diese. Nutzt für Programmcode lieber Gist oder PasteBin-Services und verlinkt die Code-Schnipsel.

Post a Comment

Ihre E-Mail wird niemals veröffentlicht oder verteilt. Benötigte Felder sind mit * markiert

*
*

Du kannst diese HTML Tags und Attribute verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>