This article has been localized into German by the community.
Mit Exceptions umgehen
In jedem Programm gibt es manchmal Fehler. In C# sind wir mit einem guten Compiler gesegnet, welcher uns helfen wird, die bekanntesten Fehler zu vermeiden. Logischerweise kann er nicht jeden Fehler erkennen, der eventuell auftreten kann und in diesen Fällen wird das .NET Framework eine Ausnahme (engl. Exception) werfen, um uns zu sagen, was fehlgeschlagen ist. In einem früheren Kapitel, über Arrays, habe ich beschrieben, wie wir eine Ausnahme erhalten, wenn wir ein Array mit zu vielen Werten füllen. Lass uns ein Beispiel ansehen:
using System;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int[] numbers = new int[2];
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
Console.ReadLine();
}
}
}
Ok, versuchen wir mal dieses Beispiel zu starten, und wir werden sehen, was gemeint ist. Erkennen Sie den Fehler? Wir haben ein Array mit Integern definiert, das Platz für 2 Werte hat, jedoch versuchen wir 3 Plätze zu nutzen. Logischerweise, führt das zu einem Fehler, der auftritt, wenn wir versuchen, das Programm zu starten. Wenn wir das Programm in Visual Studio ausführen, erhalten wir einige Optionen von der IDE angeboten, wie wir mit der Ausnahme (engl. Exception) umgehen können, wenn wir das Programm aber ausführen, indem wir einfach auf die EXE Datei doppelklicken, werden wir einen bösen Fehler bekommen. Wenn wir wissen, dass ein Fehler auftreten könnte, sollten wir dies im Code berücksichtigen. Das ist der Punkt, an dem Ausnahmen (engl. Exceptions) genutzt werden. Hier ist eine leicht modifizierte Version des oberen Codes:
int[] numbers = new int[2];
try
{
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
}
catch
{
Console.WriteLine("Something went wrong!");
}
Console.ReadLine();
Lass mich dir deinen neuen besten Freund vorstellen, der dir bei der Fehlerbehandlung (engl. error handling) hilft: der try..catch Block. Versuch jetzt mal das Programm zu starten, uns sieh dir den Unterschied an - anstelle dass Visual Studio/Windows uns sagt, dass ein ernsthaftes Problem aufgetreten ist, dürfen wir unsere eigene Geschichte erzählen. Aber wäre es nicht toll, wenn wir sagen könnten, was falsch gelaufen ist? Kein Problem:
catch(Exception ex)
{
Console.WriteLine("An error occured: " + ex.Message);
}
Wie du sehen kannst, haben wir etwas zu der catch Aussage hinzugefügt. Jetzt sagen wir, welche Ausnahme (engl. Exception) wir fangen wollen, in diesem Fall die Basisklasse aller Ausnahmen, die Klasse Exception. Wenn wir das machen, bekommen wir ein paar Informationen darüber, welches Problem die Ausnahme verursacht hat. Und wenn wir die Nachricht ausgeben lassen, erhalten wir eine verständliche Beschreibung des Problems.
Wie ich bereits sagte, ist Exception die allgemeinste Art von Ausnahme. Die Regeln der Ausnahmebehandlung sagen uns, dass wir immer die allgemeinste Art von Ausnahme verwenden sollten, und in diesem Fall kennen wir tatsächlich die genaue Art der Ausnahme, die durch unseren Code erzeugt wird. Wie? Weil Visual Studio uns gesagt hat, als wir sie noch nicht behandelt hatten. Im Zweifelsfall beschreibt die Dokumentation meist, welche Ausnahme(n) eine Methode auslösen kann. Eine weitere Möglichkeit, es herauszufinden, ist, die Klasse Exception zu verwenden, um es uns mitzuteilen - ändern wir die Ausgabezeile entsprechend:
Console.WriteLine("An error occured: " + ex.GetType().ToString());
Das Ergebnis ist erwartungsgemäß eine IndexOutOfRangeException. Wir sollten daher diese Ausnahme behandeln, aber nichts hindert uns daran, mehr als eine Ausnahme zu behandeln. In einigen Situationen kannst du verschiedene Dinge tun, je nachdem, welche Ausnahme genau ausgelöst wurde. Ändern wir einfach unseren catch-Block wie folgt:
catch(IndexOutOfRangeException ex)
{
Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
Console.WriteLine("Some sort of error occured: " + ex.Message);
}
Wie wir sehen können, suchen wir zuerst nach der IndexOutOfRangeException. Wenn wir es umgekehrt machen würden, würde der Catch-Block mit der Klasse 'Exception' es bekommen, da alle Ausnahmen davon abgeleitet sind. Mit anderen Worten, wir sollten zuerst die spezifischsten Ausnahmen verwenden.
Eine weitere Sache, die Sie über Ausnahmen wissen sollten, ist der finally-Block. Der finally-Block kann zu einem Satz von Catch-Blöcken hinzugefügt oder allein verwendet werden, je nach Bedarf. Der Code innerhalb des finally-Blocks wird immer ausgeführt - Ausnahme oder keine Ausnahme. Es ist ein guter Ort, wenn wir Dateiverweise schließen oder Objekte entsorgen müssen, die wir nicht mehr benötigen. Da unsere Beispiele bisher ziemlich einfach waren, brauchten wir keine Bereinigung, da der Garbage Collector das erledigt. Aber da es wahrscheinlich Situationen geben wird, in denen Sie den finally block benötigen, hier ist eine erweiterte Version unseres Beispiels:
int[] numbers = new int[2];
try
{
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
}
catch(IndexOutOfRangeException ex)
{
Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
Console.WriteLine("Some sort of error occured: " + ex.Message);
}
finally
{
Console.WriteLine("It's the end of our try block. Time to clean up!");
}
Console.ReadLine();
Wenn wir den Code ausführen, sehen wir, dass sowohl der erste catch-block als auch der letzte, der finally-Block ausgeführt werden. Wenn wir die Zeile entfernen, die die Zahl 42 zum Array hinzufügt, werden wir sehen, dass der letzte Block weiterhin ausgeführt wird.
Ein weiterer wichtiger Teil, den wir über Ausnahmen wissen sollten, ist, wie sie sich auf die Methode auswirken, in der die Ausnahmen auftreten. Nicht alle unbehandelten Ausnahmen sind für Ihre Anwendung fatal, aber wenn sie es nicht sind, sollten wir nicht erwarten, dass der verbleibende Code der Methode ausgeführt wird. Andererseits, wenn wir die Ausnahme behandeln, werden nur die Zeilen hinter dem Try-block ausgeführt. In unserem Beispiel wird die Schleife, die die Werte des Arrays ausgibt, nie erreicht, da der Try-Block direkt zu den catch/finally Block(s) geht, sobald eine Ausnahme ausgelöst wird. Allerdings ist die letzte Zeile erreicht, in der wir von der Konsole lesen, um zu verhindern, dass die Anwendung sofort beendet wird. Wir solltest dies immer im Hinterkopf haben, wenn wir Try-Blöcke schreiben.