Exceptions und wie man am Besten mit Ihnen umgeht. Hilft Try..Catch?
In diesem Artikel geht es darum, was die vermeintlich beste Vorgehensweise ist, wenn in einem Programm etwas unvorhergesehenes passiert und eine Exception geworfen wird. Sprich, der Programmablauf bricht einfach ab. Sollte man Try..Catch benutzen?
Nehmen wir folgendes kleine Beispiel um eine Exception zu provozieren:
Private Sub Startseite_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim sUrl As String = "C:\Pfad\Zur\NichtExistierendenDatei.txt"
Dim fileContents As String = System.IO.File.ReadAllText(sUrl)
End Sub
Wird das Programm gestartet, erhalten wir folgende Exception-Fehlermeldung:
Im schlimmsten (aber wahrscheinlichsten) Fall drückt der Benutzer jetzt auf <Weiter>. Das hat zur Folge, dass das Programm weiterläuft, so als wäre alles in Ordnung. In Wirklichkeit ist aber etwas ganz massiv schief gelaufen. Die Fehlermeldung besagt: Ich kann einen Teil des angegebenen Pfades nicht finden! Das bedeutet, dass das Dateisystem entweder defekt ist oder aber z.B. das Netzwerk komplett weg ist. Der Datenbankzugriff könnte allerdings noch funktionieren. Das wiederum könnte zur Folge haben, dass ohne den Zugriff auf die Datei und die Informationen die dort hinterlegt sind, in der Datenbank unsinnige Daten gespeichert werden. Viel Spass bei der Fehlerbereinigung…
Besser wäre es, den Fehler mit Try..Catch abzufangen und eine Meldung anzuzeigen:
Private Sub Startseite_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
' Versuch, eine nicht existierende Datei zu öffnen'
Dim sUrl As String = "C:\Pfad\Zur\NichtExistierendenDatei.txt"
Dim sContent As String = System.IO.File.ReadAllText(sURL)
Catch exFnF As System.io.FileNotFoundException
MessageBox.Show("Datei wurde nicht gefunden - " & exFnF.Message)
Exit Sub
End Try
subDatenVerarbeiten(sContent)
End Sub
Dieses Beispiel enthält aber schon das nächste Problem: Es wird nur die FileNotFoundException-Exception gefangen, die aber hier auch nicht greift. Da schon der Ordner <Pfad> nicht gefunden werden kann, wird eine IOException- und nicht eine FileNotFoundException-Exception geworfen. Nur so nebenbei: Die Funktion ‘ReadAllText’ kann 9 verschiedene Exceptions werfen. Sprich: bevor man sich die Mühe macht herauszufinden welche Exceptions geworfen werden können, sollte man am besten alle auf einmal abfangen.
Daher: Immer mit Catch ex as Exception arbeiten.
Das obige Beispiel löst aber unser Problem immer noch nicht. Der Benutzer erhält zwar jetzt die Meldung, dass die Datei nicht gefunden wurde aber interessiert ihn das? Wohl eher nicht, er wird “normal” weiterarbeiten, was zu den oben beschriebenen Problemen führen kann.
Ich bin der Meinung, dass man sich den ganzen Aufwand mit Try..Catch
sparen kann und stattdesen lieber mit einer Globalen Fehlerbehandlung arbeiten sollte, die dann auch Fehler berücksichtigt, die nicht in einem Try..Catch-Block abgefangen werden. Punkt. Aus.
In der Praxis zeigt sich allerdings, dass es nicht nur schwarz und weiss oder 1 und 0 gibt. In bestimmten Fällen kann es durchaus sinnvoll sein, mit Try..Catch zu arbeiten (wenn z.B. keine Folgefehler auftreten können, auch Swallow-Catching genannt).
Aber: In den meisten Fällen ist es unnötig. Beispiel: Eine Objet-Variable ist nicht initialisiert – es kommt zu einer NullReferenceException
. Jetzt kann man die Exception sicherlich mit Try..Catch fangen aber in 99% der Fälle handelt es sich um einen Programmierfehler im Vorfeld. Wie sonst sollte eine Variable nicht initialisiert sein? Sollte es tatsächlich aber einmal eine Situation geben in der der Fall eintreten kann, dann sollte man besser die Variable mit IsNothing abfragen und nicht mit Try..Catch arbeiten.
Wie kann die Lösung aussehen?
Ich mache es mittlerweile so, dass ich bei Datei-, Datenbank- oder Netzwerzugriffen, also dann, wenn es vorhersehbar ist das Fehler auftreten können, mit Try..Catch
arbeite. Ansonsten nicht (abgesehen von ganz wenigen Ausnahmen)! Im Catch-Block generiere ich eine, für den Benutzer sinnvolle, Fehlermeldung und diese reiche ich dann an die zentrale Fehlerbehandlung weiter. Dort wird sie dem User angezeigt und gleichzeitig wird die Original-Fehlermeldung von Visaul Studio in einer Datei protokolliert. Anschliessend wird das Programm beendet (Graceful Exit genannt).
Um es nochmal ganz deutlich zu sagen: Die Behandlung einer Exception mithilfe von Try..Catch ist per se nichts schlechtes aber meist überflüssig.
Wer sich näher mit dem Thema beschäftigen möchte, dem empfehle ich den Beitrag TryCatch ist ein heißes Eisen von ErfinderDesRades im Forum VB-Paradise. Wie schreibt er so schön: Fange nur eine Exception, die du auch behandeln kannst!
Wie man die Fehlerbehandlung programmtechnisch in VB.Net umsetzen kann, zeige ich in einem weiteren Artikel.
Leave a Reply