Jeder kann coden / Programmieren & TicTacToe / C# Einführung
Exceptions¶
Die Seite behandelt die Grundlagen von Exceptions (Ausnahmen) in C# und erklärt, wie Fehler in Programmen behandelt werden können. Hier sind die wichtigsten Inhalte und Beispiele, kommentiert und erklärt:
1. Einführung in Exceptions¶
Exceptions sind Ereignisse, die während der Laufzeit auftreten und das normale Ausführen eines Programms unterbrechen. Sie werden verwendet, um Fehler zu erkennen und zu behandeln, ohne dass das Programm abstürzt.
2. try
, catch
, finally
-Blöcke¶
Exceptions werden in C# oft mit try
, catch
und finally
behandelt. Ein try
-Block enthält den Code, der möglicherweise eine Ausnahme auslöst. catch
fängt die Ausnahme ab und erlaubt es, den Fehler zu behandeln, und finally
führt Code aus, der unabhängig davon läuft, ob eine Ausnahme auftrat oder nicht.
Beispiel 1: Grundstruktur mit try
, catch
, und finally
¶
try
{
int divisor = 0;
int result = 10 / divisor; // Dies löst eine Division durch Null Ausnahme aus
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Fehler: Division durch Null ist nicht erlaubt."); // Fehlerbehandlung
}
finally
{
Console.WriteLine("Dieser Block wird immer ausgeführt, egal ob eine Ausnahme auftritt oder nicht."); // Cleanup-Code
}
Fehler: Division durch Null ist nicht erlaubt. Dieser Block wird immer ausgeführt, egal ob eine Ausnahme auftritt oder nicht.
- Kommentar: In diesem Beispiel wird versucht, durch Null zu teilen, was eine
DivideByZeroException
auslöst. Dercatch
-Block fängt die Ausnahme ab und gibt eine Fehlermeldung aus. Derfinally
-Block wird am Ende immer ausgeführt und kann zur Bereinigung genutzt werden.
3. Spezifische Exceptions behandeln¶
In C# können verschiedene Exception-Typen auftreten, wie ArgumentNullException
, FileNotFoundException
und viele mehr. Man kann mehrere catch
-Blöcke verwenden, um verschiedene Exception-Typen zu behandeln.
Beispiel 2: Mehrere catch
-Blöcke¶
try
{
string[] names = null;
Console.WriteLine(names[0]); // Zugriff auf null-Array löst eine NullReferenceException aus
}
catch (NullReferenceException ex)
{
Console.WriteLine("Fehler: Es wurde auf ein null-Array zugegriffen.");
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Fehler: Index außerhalb des Bereichs.");
}
Fehler: Es wurde auf ein null-Array zugegriffen.
- Kommentar: Hier versucht das Programm, auf ein null-Array zuzugreifen, was eine
NullReferenceException
auslöst. Der entsprechendecatch
-Block behandelt diesen Fehler. Würde der Fehler nicht übereinstimmen, könnte ein anderercatch
-Block einspringen.
4. Benutzerdefinierte Exceptions¶
Man kann eigene Exception-Typen erstellen, um spezifische Fehlerfälle in eigenen Anwendungen zu kennzeichnen. Hierzu wird von der Exception
-Klasse abgeleitet.
Beispiel 3: Benutzerdefinierte Exception¶
public class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message) { }
}
try
{
int age = -5;
if (age < 0)
{
throw new InvalidAgeException("Alter kann nicht negativ sein."); // Benutzerdefinierte Exception
}
}
catch (InvalidAgeException ex)
{
Console.WriteLine($"Benutzerdefinierte Exception abgefangen: {ex.Message}");
}
Benutzerdefinierte Exception abgefangen: Alter kann nicht negativ sein.
- Kommentar: Die benutzerdefinierte Exception
InvalidAgeException
wird erstellt und genutzt, um ein negatives Alter abzufangen. Dies ermöglicht spezifische Fehlerbehandlungen.
5. Die throw
-Anweisung¶
Mit throw
kann eine Exception explizit ausgelöst werden. Dies ist nützlich, wenn bestimmte Bedingungen nicht erfüllt sind.
Beispiel 4: Verwendung von throw
¶
public static void CheckPositive(int number)
{
if (number <= 0)
{
throw new ArgumentException("Die Zahl muss positiv sein."); // Ausnahme wird explizit ausgelöst
}
}
try
{
CheckPositive(-1);
}
catch (ArgumentException ex)
{
Console.WriteLine($"Fehler: {ex.Message}");
}
Fehler: Die Zahl muss positiv sein.
- Kommentar: Die Methode
CheckPositive
prüft, ob eine Zahl positiv ist. Wenn die Zahl negativ oder null ist, wird eineArgumentException
mit einer Nachricht ausgelöst.
6. Exception-Wiederverwendung¶
In einem catch
-Block kann throw;
ohne Argument genutzt werden, um die aktuelle Exception erneut auszulösen, sodass sie von anderen Teilen des Programms weiterverarbeitet werden kann.
Beispiel 5: Exception-Wiederverwendung¶
try
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Ausnahme auslösen
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Fehler in innerem Block.");
throw; // Ausnahme weitergeben
}
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Fehler im äußeren Block.");
}
Fehler in innerem Block. Fehler im äußeren Block.
- Kommentar: Im inneren
catch
-Block wird die Ausnahme weitergegeben, um sie im äußerencatch
-Block erneut zu behandeln.
7. Ausnahmeinformationen anzeigen¶
Exceptions besitzen Eigenschaften wie Message
, StackTrace
und InnerException
, um zusätzliche Informationen bereitzustellen.
Beispiel 6: Eigenschaften einer Exception¶
public void methodeMitException()
{
try
{
int divisor = 0;
int result = 10 / divisor;
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Fehler: {ex.Message}"); // Fehlermeldung
Console.WriteLine($"Stapelverfolgung: {ex.StackTrace}"); // StackTrace
}
}
methodeMitException();
Fehler: Attempted to divide by zero. Stapelverfolgung: at Submission#22.methodeMitException()
try
{
int value = -10;
if (value < 0)
{
throw new ArgumentOutOfRangeException("value", "Wert darf nicht negativ sein.");
}
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "value")
{
Console.WriteLine("Der Wert-Parameter war negativ.");
}
Der Wert-Parameter war negativ.
- Kommentar: Hier wird ein
catch
-Block nur ausgeführt, wenn die Bedingungex.ParamName == "value"
erfüllt ist. Das ermöglicht eine gezielte Behandlung.
Cheatsheet Exceptions¶
Vertiefung¶
Das Thema Exceptions in C# ist umfassend und bietet viele weitere Aspekte, die ergänzend behandelt werden können, um ein vollständiges Verständnis zu entwickeln. Hier einige zusätzliche Punkte:
1. Best Practices bei der Fehlerbehandlung¶
Nur erwartete Ausnahmen abfangen: Fangen Sie nur solche Ausnahmen ab, die Sie sinnvoll behandeln können. Allgemeine
catch (Exception)
-Blöcke sollten vermieden werden, es sei denn, sie sind gut begründet.Spezifischere Ausnahmen zuerst behandeln: Wenn mehrere
catch
-Blöcke verwendet werden, sollten spezifischere Ausnahmen vor allgemeine gesetzt werden.try { // Code } catch (ArgumentOutOfRangeException ex) { // Behandlung spezifischer Ausnahme } catch (Exception ex) { // Allgemeine Ausnahmebehandlung }
Vermeidung von
catch
ohne Aktion: Leerecatch
-Blöcke führen dazu, dass Fehler ignoriert werden, was Debugging und Wartung erschwert.
2. Performance-Überlegungen¶
Das Auslösen und Abfangen von Ausnahmen ist ressourcenintensiv. Verwenden Sie Ausnahmen daher nur für Fehlerfälle und nicht für Kontrollfluss.
Beispiel (schlecht):
try { int index = Array.FindIndex(array, item => item == value); if (index == -1) throw new Exception("Wert nicht gefunden."); } catch { // Alternative Verarbeitung }
Besser:
int index = Array.FindIndex(array, item => item == value); if (index == -1) { Console.WriteLine("Wert nicht gefunden."); }
3. Ressourcenverwaltung mit using
¶
Für Objekte, die Ressourcen belegen (z. B. Datei- oder Datenbankzugriffe), kann using
verwendet werden. Es stellt sicher, dass die Ressourcen auch im Fehlerfall ordnungsgemäß freigegeben werden.
using (StreamReader reader = new StreamReader("file.txt"))
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
// Ressourcen werden hier automatisch freigegeben
- Alternative mit
try
-finally
: Fallsusing
nicht verwendet wird, kann einfinally
-Block zur Freigabe genutzt werden.
4. Unbehandelte Ausnahmen¶
Globaler Exception-Handler: Für Anwendungen (insbesondere GUIs oder Server) kann ein globaler Exception-Handler definiert werden, um unbehandelte Ausnahmen abzufangen.
Beispiel (Konsolenanwendung):
AppDomain.CurrentDomain.UnhandledException += (sender, args) => { Console.WriteLine($"Unbehandelte Ausnahme: {args.ExceptionObject}"); };
5. Asynchrone Fehlerbehandlung¶
Bei asynchronen Methoden (z. B. async
/await
) ist die Fehlerbehandlung ein wenig anders:
Exceptions in asynchronen Methoden können mit
try
-catch
abgefangen werden.try { await SomeAsyncMethod(); } catch (Exception ex) { Console.WriteLine($"Fehler: {ex.Message}"); }
Exceptions, die in
Task
-Objekten auftreten, können mitTask.Wait
oderTask.Result
abgefangen werden.
6. Dokumentation von Exceptions¶
XML-Kommentare: Methoden, die Exceptions auslösen können, sollten dies in den XML-Kommentaren dokumentieren. Dies hilft Entwicklern, sich auf mögliche Fehler vorzubereiten.
/// <summary> /// Führt eine Berechnung aus. /// </summary> /// <param name="value">Ein Wert für die Berechnung.</param> /// <exception cref="ArgumentOutOfRangeException">Wenn der Wert negativ ist.</exception> public void Calculate(int value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); }
7. Serialisierbare Exceptions¶
Benutzerdefinierte Exceptions sollten mit [Serializable]
markiert und korrekt serialisierbar gemacht werden, um in verteilten Anwendungen genutzt werden zu können.
[Serializable]
public class MyCustomException : Exception
{
public MyCustomException() { }
public MyCustomException(string message) : base(message) { }
public MyCustomException(string message, Exception inner) : base(message, inner) { }
protected MyCustomException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
8. Exception-Hierarchie¶
Die Klassenhierarchie für Exceptions in .NET hilft, spezifische Ausnahmen besser zu verstehen:
System.Exception
: Basisklasse für alle Ausnahmen.System.SystemException
: Basisklasse für Ausnahmen, die vom Laufzeitsystem ausgelöst werden.System.ApplicationException
: Kann für benutzerdefinierte Ausnahmen verwendet werden, wird jedoch selten genutzt.
9. Logging von Exceptions¶
Fehler sollten idealerweise protokolliert werden, um sie später analysieren zu können. Tools wie Serilog
, NLog
oder das .NET-Built-in Logging Framework können dafür verwendet werden.
try
{
throw new InvalidOperationException("Fehlerhafte Operation.");
}
catch (Exception ex)
{
// Beispiel mit Serilog
Log.Error(ex, "Ein Fehler ist aufgetreten.");
}