Empfohlene Vorgehensweise für die Ausnahmebehandlung
Ein gut entworfener Satz von Fehlerbehandlungsblöcken kann einem Programm Stabilität verleihen und die Absturzgefahr mindern, da die Anwendung solche Fehler behandeln kann. Die folgende Auflistung enthält Vorschläge für die besten Methoden zur Ausnahmebehandlung:
Sie sollten sich klarmachen, wann ein try/catch-Block eingesetzt wird. Sie können zum Beispiel programmgesteuert auf einen wahrscheinlich auftretenden Fehlerzustand prüfen, ohne die Fehlerbehandlung zu verwenden. Dagegen ist es in anderen Fällen angebracht, die Fehlerbehandlung zu verwenden, um den Fehlerzustand abzufangen.
Im folgenden Beispiel wird mit einer if-Anweisung überprüft, ob eine Verbindung geschlossen ist. Sie können diese Methode verwenden, anstatt eine Ausnahme auszulösen, wenn die Verbindung nicht geschlossen ist.
If conn.State <> ConnectionState.Closed Then
conn.Close()
End IF
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
if (conn->State != ConnectionState::Closed)
{
conn->Close();
}
Im folgenden Beispiel wird eine Ausnahme ausgelöst, wenn die Verbindung nicht geschlossen ist.
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
try
{
conn->Close();
}
catch (InvalidOperationException^ ex)
{
Console::WriteLine(ex->GetType()->FullName);
Console::WriteLine(ex->Message);
}
Die Wahl der Methode hängt von der erwarteten Häufigkeit des Ereignisses ab. Ist das Ereignis ein wirklich seltener Fehler (wie z. B. ein unerwartetes Dateiende), ist eine Fehlerbehandlung besser, da normalerweise weniger Code ausgeführt wird. Kommt das Ereignis hingegen regelmäßig vor, ist die programmgesteuerte Methode der Fehlerüberprüfung angebrachter. Tritt in diesem Fall eine Ausnahme auf, nimmt die Ausnahmebehandlung mehr Zeit in Anspruch.
Umgeben Sie den Code, der potenzielle Ausnahmen generieren kann, mit try/finally-Blöcken, und fassen Sie alle catch-Anweisungen an einer Stelle zusammen. Auf diese Weise wird die Ausnahme durch die try-Anweisung generiert, durch die finally-Anweisung werden die Ressourcen geschlossen oder freigegeben, und durch die catch-Anweisung wird die Ausnahme von zentraler Stelle aus behandelt.
Ordnen Sie Ausnahmen in catch-Blöcken immer von der spezifischsten bis zur allgemeinsten an. Durch dieses Verfahren werden spezifische Ausnahmen behandelt, bevor sie an einen allgemeineren catch-Block übergeben werden.
Lassen Sie die Namen von Ausnahmeklassen auf das Wort "Exception" enden. Beispiel:
Public Class MyFileNotFoundException
Inherits Exception
End Class
public class MyFileNotFoundException : Exception
{
}
public ref class MyFileNotFoundException : public Exception
{
};
Achten Sie beim Erstellen von benutzerdefinierten Ausnahmen darauf, dass Metadaten der Ausnahmen für remote ausgeführten Code verfügbar sind, und zwar auch bei Ausnahmen, die über verschiedene Anwendungsdomänen hinweg auftreten. Ein Beispiel: Anwendungsdomäne A erstellt Anwendungsdomäne B, die wiederum Code ausführt, der eine Ausnahme auslöst. Damit die Anwendungsdomäne A die Ausnahme korrekt erfasst und behandelt, muss die Assembly gefunden werden, die die durch die Anwendungsdomäne B ausgelöste Ausnahme enthält. Wenn die Anwendungsdomäne B eine Ausnahme auslöst, die in einer Assembly unter ihrer Anwendungsbasis enthalten ist, aber nicht unter der Anwendungsbasis von A, kann die Anwendungsdomäne A die Ausnahme nicht finden. Daraufhin wird von der CLR eine FileNotFoundException-Ausnahme ausgelöst. Um diese Situation zu vermeiden, haben Sie zwei Möglichkeiten, die Assembly mit den Ausnahmeinformationen bereitzustellen:
Legen Sie die Assembly in einer gemeinsamen Anwendungsbasis ab, die sich beide Anwendungsdomänen teilen.
- oder -
Wenn die Domänen keine gemeinsame Anwendungsbasis verwenden, signieren Sie die Assembly, in der die Ausnahmeinformationen enthalten sind, mit einem starken Namen und legen sie in einem globalen Assemblycache ab.
Verwenden Sie in C# und C++ mindestens die drei allgemeinen Konstruktoren, wenn Sie eigene Ausnahmeklassen erstellen. Ein Beispiel finden Sie unter Gewusst wie: Erstellen benutzerdefinierter Ausnahmen.
Verwenden Sie in erster Linie die vordefinierten Ausnahmetypen. Definieren Sie neue Ausnahmetypen nur für programmgesteuerte Szenarien. Führen Sie eine neue Ausnahmeklasse ein, um Programmierern zu ermöglichen, darauf basierenden Code anders zu behandeln.
Leiten Sie benutzerdefinierte Ausnahmen für die meisten Anwendungen von der Exception-Klasse ab. Ursprünglich wurde davon ausgegangen, dass benutzerdefinierte Ausnahmen von der ApplicationException-Klasse abgeleitet werden sollten. In der Praxis hat sich dies jedoch nicht als bedeutender Vorteil erwiesen.
Schließen Sie eine lokalisierte Beschreibungszeichenfolge in jeder Ausnahme ein. Für Benutzer sichtbare Fehlermeldungen werden eher von der Beschreibungszeichenfolge der ausgelösten Ausnahme abgeleitet als von der Ausnahmeklasse.
Verwenden Sie grammatisch korrekte Fehlermeldungen, einschließlich Zeichensetzung am Satzende. Jeder Satz in einer Beschreibungszeichenfolge sollte mit einem Punkt beendet werden.
Geben Sie Exception-Eigenschaften für den programmgesteuerten Zugriff an. Fügen Sie einer Ausnahme nur dann Informationen zusätzlich zur Beschreibungszeichenfolge bei, wenn es ein programmgesteuertes Szenario gibt, in dem dieser Zusatz sinnvoll ist.
Geben Sie bei sehr häufig auftretenden Fehlern NULL zurück. Beispielsweise gibt Open null zurück, wenn die Datei nicht gefunden wurde. Ist die Datei dagegen gesperrt, wird eine Ausnahme ausgelöst.
Entwerfen Sie Klassen, damit eine Ausnahme niemals während normaler Verwendung ausgelöst wird. Eine FileStream-Klasse stellt beispielsweise eine andere Methode zur Verfügung, mit der ermittelt werden kann, ob das Ende der Datei erreicht wurde. Dadurch wird die Ausnahme vermieden, die bei dem Versuch ausgelöst wird, nach Erreichen des Dateiendes weiterzulesen. Das folgende Beispiel zeigt, wie bis zum Dateiende gelesen wird.
Class FileRead
Public Sub ReadAll(fileToRead As FileStream)
' This if statement is optional
' as it is very unlikely that
' the stream would ever be null.
If fileToRead Is Nothing Then
Throw New System.ArgumentNullException()
End If
Dim b As Integer
' Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin)
' Read each byte to the end of the file.
For i As Integer = 0 To fileToRead.Length - 1
b = fileToRead.ReadByte()
Console.Write(b.ToString())
' Or do something else with the byte.
Next i
End Sub
End Class
class FileRead
{
public void ReadAll(FileStream fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == null)
{
throw new System.ArgumentNullException();
}
int b;
// Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}
class FileRead
{
public:
void ReadAll(FileStream^ fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == nullptr)
{
throw gcnew System::ArgumentNullException();
}
int b;
// Set the stream position to the beginning of the file.
fileToRead->Seek(0, SeekOrigin::Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead->Length; i++)
{
b = fileToRead->ReadByte();
Console::Write(b.ToString());
// Or do something else with the byte.
}
}
};
Lösen Sie eine InvalidOperationException aus, wenn eine festgelegte Eigenschaft oder ein Methodenaufruf aufgrund des aktuellen Status des Objekts nicht geeignet ist.
Lösen Sie eine ArgumentException aus oder eine von ArgumentException abgeleitete Klasse, wenn ungültige Parameter übergeben werden.
Die Stapelüberwachung beginnt mit der Anweisung, bei der die Ausnahme ausgelöst wurde, und endet mit der catch-Anweisung, mit der die Ausnahme abgefangen wird. Achten Sie auf diesen Umstand, wenn Sie eine throw-Anweisung platzieren.
Verwenden Sie Methoden zum Generieren von Ausnahmen. Es ist üblich, dass eine Klasse von unterschiedlichen Punkten in der Implementierung aus die gleiche Ausnahme auslöst. Verwenden Sie Hilfsmethoden, die eine Ausnahme erstellen und zurückgeben, um ausufernden Code zu vermeiden. Beispiel:
Class FileReader
Private fileName As String
Public Sub New(path As String)
fileName = path
End Sub
Public Function Read(bytes As Integer) As Byte()
Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
If results Is Nothing
Throw NewFileIOException()
End If
Return results
End Function
Function NewFileIOException() As FileReaderException
Dim description As String = "My NewFileIOException Description"
Return New FileReaderException(description)
End Function
End Class
class FileReader
{
private string fileName;
public FileReader(string path)
{
fileName = path;
}
public byte[] Read(int bytes)
{
byte[] results = FileUtils.ReadFromFile(fileName, bytes);
if (results == null)
{
throw NewFileIOException();
}
return results;
}
FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
return new FileReaderException(description);
}
}
ref class FileReader
{
private:
String^ fileName;
public:
FileReader(String^ path)
{
fileName = path;
}
array<Byte>^ Read(int bytes)
{
array<Byte>^ results = FileUtils::ReadFromFile(fileName, bytes);
if (results == nullptr)
{
throw NewFileIOException();
}
return results;
}
FileReaderException^ NewFileIOException()
{
String^ description = "My NewFileIOException Description";
return gcnew FileReaderException(description);
}
};
Sie können auch den Ausnahmekonstruktor verwenden, um eine Ausnahme zu erstellen. Dies ist bei globalen Ausnahmeklassen wie der ArgumentException-Klasse eher angebracht.
Lösen Sie Ausnahmen aus, anstatt einen Fehlercode oder HRESULT zurückzugeben.
Löschen Sie Zwischenergebnisse, wenn Sie eine Ausnahme auslösen. Aufrufer sollten davon ausgehen können, dass keine Nebeneffekte auftreten, wenn eine Ausnahme von einer Methode ausgelöst wird.