Best Practices für AusnahmenBest practices for exceptions

Eine ausgereifte App behandelt Ausnahmen und Fehler, um App-Abstürze zu verhindern.A well-designed app handles exceptions and errors to prevent app crashes. In diesem Abschnitt werden Best Practices für die Behandlung und Erstellung von Ausnahmen beschrieben.This section describes best practices for handling and creating exceptions.

Verwenden Sie Try/Catch/Finally-Blöcke zum Beheben von Fehlern oder zum Freigeben von Ressourcen.Use try/catch/finally blocks to recover from errors or release resources

Betten Sie Code, der Ausnahmen erzeugen kann, in try/catch-Blöcke ein, damit ihr Code nach diesen Ausnahmen wiederhergestellt werden kann.Use try/catch blocks around code that can potentially generate an exception and your code can recover from that exception. Ordnen Sie Ausnahmen in catch-Blöcken immer von der am stärksten abgeleiteten zur am wenigsten abgeleiteten.In catch blocks, always order exceptions from the most derived to the least derived. Alle Ausnahmen sind von Exception abgeleitet.All exceptions derive from Exception. Stärker abgeleitete Ausnahmen werden nicht durch eine catch-Klausel verarbeitet, der eine catch-Klausel für eine Ausnahmebasisklasse vorangestellt ist.More derived exceptions are not handled by a catch clause that is preceded by a catch clause for a base exception class. Wenn Ihr Code aus einer Ausnahme nicht wiederhergestellt werden kann, fangen Sie diese Ausnahme nicht ab.When your code cannot recover from an exception, don't catch that exception. Aktivieren Sie Methoden weiter oben in der Aufrufliste, um nach Möglichkeit eine Wiederherstellung auszuführen.Enable methods further up the call stack to recover if possible.

Bereinigen Sie die Ressourcen, die mit using-Anweisungen oder finally-Blöcken zugeordnet wurden.Clean up resources allocated with either using statements, or finally blocks. Bevorzugen Sie using-Anweisungen, um Ressourcen automatisch zu bereinigen, wenn Ausnahmen ausgelöst werden.Prefer using statements to automatically clean up resources when exceptions are thrown. Verwenden Sie finally-Blöcke, um Ressourcen zu bereinigen, die IDisposable nicht implementieren.Use finally blocks to clean up resources that don't implement IDisposable. Code in einer finally-Klausel wird fast immer – d. h. auch bei Auslösen einer Ausnahme – ausgeführt.Code in a finally clause is almost always executed even when exceptions are thrown.

Behandeln häufig auftretender Bedingungen ohne Auslösen von AusnahmenHandle common conditions without throwing exceptions

Bedingungen, deren Auftreten wahrscheinlich ist, die aber möglicherweise eine Ausnahme auslösen, sollten Sie so behandeln, dass die Ausnahme vermieden wird.For conditions that are likely to occur but might trigger an exception, consider handling them in a way that will avoid the exception. Wenn Sie z. B. versuchen, eine bereits geschlossene Verbindung erneut zu schließen, erhalten Sie eine InvalidOperationException.For example, if you try to close a connection that is already closed, you'll get an InvalidOperationException. Dies können Sie verhindern, indem Sie eine if-Anweisung verwenden, um den Verbindungsstatus zu überprüfen, bevor Sie versuchen, die Verbindung zu schließen.You can avoid that by using an if statement to check the connection state before trying to close it.

if (conn->State != ConnectionState::Closed)
{
    conn->Close();
}
if (conn.State != ConnectionState.Closed)
{
    conn.Close();
}
If conn.State <> ConnectionState.Closed Then
    conn.Close()
End IF

Wenn Sie den Verbindungsstatus vor dem Schließen der Verbindung nicht überprüfen, können Sie die InvalidOperationException abfangen.If you don't check connection state before closing, you can catch the InvalidOperationException exception.

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);
}
Try
    conn.Close()
Catch ex As InvalidOperationException
    Console.WriteLine(ex.GetType().FullName)
    Console.WriteLine(ex.Message)
End Try

Die Wahl der Methode hängt von der erwarteten Häufigkeit des Ereignisses ab.The method to choose depends on how often you expect the event to occur.

  • Verwenden Sie die Ausnahmebehandlung, wenn das Ereignis nicht sehr häufig auftritt, d.  h. wenn es wirklich ungewöhnlich ist und auf einen Fehler hinweist (z. B. ein unerwartetes Dateiende).Use exception handling if the event doesn't occur very often, that is, if the event is truly exceptional and indicates an error (such as an unexpected end-of-file). Wenn Sie die Ausnahmebehandlung verwenden, wird weniger Code in normalen Bedingungen ausgeführt.When you use exception handling, less code is executed in normal conditions.

  • Wenn das Ereignis regelmäßig auftritt und als Teil der normalen Programmausführung betrachtet werden kann, suchen Sie im Code nach Fehlerbedingungen.Check for error conditions in code if the event happens routinely and could be considered part of normal execution. Wenn Sie auf gängige Fehlerbedingungen prüfen, wird weniger Code ausgeführt, da Ausnahmen vermieden werden.When you check for common error conditions, less code is executed because you avoid exceptions.

Entwerfen von Klassen mit dem Ziel, Ausnahmen zu vermeidenDesign classes so that exceptions can be avoided

Eine Klasse kann Methoden oder Eigenschaften bereitstellen, mit deren Hilfe ein Aufruf vermieden werden kann, der andernfalls eine Ausnahme auslösen würde.A class can provide methods or properties that enable you to avoid making a call that would trigger an exception. Beispielsweise stellt eine FileStream-Klasse Methoden bereit, mithilfe derer bestimmt werden kann, ob das Ende der Datei erreicht wurde.For example, a FileStream class provides methods that help determine whether the end of the file has been reached. Dadurch kann die Ausnahme vermieden werden, die andernfalls durch den Versuch ausgelöst würde, nach Erreichen des Dateiendes weiterzulesen.These can be used to avoid the exception that is thrown if you read past the end of the file. Das folgende Beispiel zeigt, wie eine Datei bis zum Ende gelesen werden kann, ohne dass eine Ausnahme ausgelöst wird.The following example shows how to read to the end of a file without triggering an exception.

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.
        }
    }
};
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 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 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

Eine andere Möglichkeit zum Vermeiden von Ausnahmen besteht darin, bei sehr häufig auftretenden Fehlern NULL (oder einen Standardwert) zurückzugeben, statt eine Ausnahme auszulösen.Another way to avoid exceptions is to return null (or default) for extremely common error cases instead of throwing an exception. Ein sehr häufig auftretender Fehler kann durchaus als normale Ablaufsteuerung betrachtet werden.An extremely common error case can be considered normal flow of control. Indem Sie in diesen Fällen NULL (oder einen Standardwert) zurückgeben, minimieren Sie die Auswirkungen auf die Leistung einer App.By returning null (or default) in these cases, you minimize the performance impact to an app.

Ob Sie bei Werttypen Nullable<T> oder einen Standardwert als Fehlerindikator verwenden, richtet sich nach Ihrer speziellen App.For value types, whether to use Nullable<T> or default as your error indicator is something to consider for your particular app. Durch Verwendung von Nullable<Guid> wird default zu null statt zu Guid.Empty.By using Nullable<Guid>, default becomes null instead of Guid.Empty. Manchmal wird durch Hinzufügen von Nullable<T> klarer, ob ein Wert vorhanden oder nicht vorhanden ist.Some times, adding Nullable<T> can make it clearer when a value is present or absent. Andererseits kann das Hinzufügen von Nullable<T> dazu führen, dass zusätzliche Fälle geprüft werden müssen, die eigentlich nicht notwendig sind und nur zu potenzielle Fehlerquellen führen.Other times, adding Nullable<T> can create extra cases to check that aren't necessary, and only serve to create potential sources of errors.

Auslösen von Ausnahmen statt Zurückgeben eines FehlercodesThrow exceptions instead of returning an error code

Ausnahmen sorgen dafür, dass Fehler nicht unbemerkt bleiben, nur weil der aufrufende Code einen Rückgabecode nicht überprüft hat.Exceptions ensure that failures do not go unnoticed because calling code didn't check a return code.

Verwenden der vordefinierten .NET-AusnahmetypenUse the predefined .NET exception types

Verwenden Sie eine neue Ausnahmeklasse nur dann, wenn sich keine vordefinierte Klasse anbietet.Introduce a new exception class only when a predefined one doesn't apply. Beispiel:For example:

  • Lösen Sie eine InvalidOperationException immer dann aus, wenn aufgrund des aktuellen Status des Objekts weder ein Eigenschaftensatz noch ein Methodenaufruf geeignet sind.Throw an InvalidOperationException exception if a property set or method call is not appropriate given the object's current state.

  • Lösen Sie eine ArgumentException oder eine der von ArgumentException abgeleiteten vordefinierten Klassen in dem Fall aus, dass ungültige Parameter übergeben werden.Throw an ArgumentException exception or one of the predefined classes that derive from ArgumentException if invalid parameters are passed.

Verwenden des Worts Exception am Ende von AusnahmeklassennamenEnd exception class names with the word Exception

Wenn eine benutzerdefinierte Ausnahme erforderlich ist, benennen Sie diese entsprechend, und leiten Sie sie von der Exception-Klasse ab.When a custom exception is necessary, name it appropriately and derive it from the Exception class. Beispiel:For example:

public ref class MyFileNotFoundException : public Exception
{
};
public class MyFileNotFoundException : Exception
{
}
Public Class MyFileNotFoundException
    Inherits Exception
End Class

Einschließen von drei Konstruktoren in benutzerdefinierte AusnahmeklassenInclude three constructors in custom exception classes

Verwenden Sie beim Erstellen eigener Ausnahmeklassen mindestens die drei gängigen Konstruktoren: den parameterlosen Konstruktor, einen Konstruktor, der eine Zeichenfolgenmeldung entgegennimmt, und einen Konstruktor, der eine Zeichenfolgenmeldung und eine innere Ausnahme entgegennimmt.Use at least the three common constructors when creating your own exception classes: the parameterless constructor, a constructor that takes a string message, and a constructor that takes a string message and an inner exception.

Ein Beispiel finden Sie unter Gewusst wie: Erstellen benutzerdefinierter Ausnahmen.For an example, see How to: Create User-Defined Exceptions.

Sicherstellen, dass Ausnahmedaten bei der Remoteausführung von Code verfügbar sindEnsure that exception data is available when code executes remotely

Stellen Sie beim Erstellen benutzerdefinierter Ausnahmen sicher, dass die Metadaten für die Ausnahmen für remote ausgeführten Code verfügbar sind.When you create user-defined exceptions, ensure that the metadata for the exceptions is available to code that is executing remotely.

In .NET-Implementierungen, die App-Domänen unterstützen, können Ausnahmen beispielsweise über mehrere App-Domänen hinweg auftreten.For example, on .NET implementations that support App Domains, exceptions may occur across App domains. Nehmen wir an, App-Domäne A erstellt App-Domäne B, und in App-Domäne B wird Code ausgeführt, der eine Ausnahme auslöst.Suppose App Domain A creates App Domain B, which executes code that throws an exception. Damit App-Domäne A die Ausnahme ordnungsgemäß abfängt und behandelt, muss sie die Assembly finden, in der die durch App-Domäne B ausgelöste Ausnahme enthalten ist. Wenn App-Domäne B eine Ausnahme auslöst, die in einer Assembly in ihrer Anwendungsbasis, nicht aber in der Anwendungsbasis von App-Domäne A enthalten ist, kann App-Domäne A die Ausnahme nicht finden. Daraufhin löst die Common Language Runtime eine FileNotFoundException aus.For App Domain A to properly catch and handle the exception, it must be able to find the assembly that contains the exception thrown by App Domain B. If App Domain B throws an exception that is contained in an assembly under its application base, but not under App Domain A's application base, App Domain A will not be able to find the exception, and the common language runtime will throw a FileNotFoundException exception. Um diese Situation zu vermeiden, haben Sie zwei Möglichkeiten, die Assembly mit den Ausnahmeinformationen bereitzustellen:To avoid this situation, you can deploy the assembly that contains the exception information in two ways:

  • Sie legen die Assembly in einer gemeinsamen Anwendungsbasis ab, die von beiden App-Domänen verwendet wird.Put the assembly into a common application base shared by both app domains.

    - oder -- or -

  • Sofern 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.If the domains do not share a common application base, sign the assembly that contains the exception information with a strong name and deploy the assembly into the global assembly cache.

Verwenden grammatisch korrekter FehlermeldungenUse grammatically correct error messages

Verfassen Sie klar verständliche Meldungen und achten Sie vor allem am Ende eines Satzes auf korrekte Satzzeichen.Write clear sentences and include ending punctuation. Jeder Satz in der Zeichenfolge, der der Exception.Message-Eigenschaft zugeordnet ist, muss mit einem Punkt enden.Each sentence in the string assigned to the Exception.Message property should end in a period. So ist der Satz „In der Protokolltabelle ist ein Überlauf aufgetreten.“For example, "The log table has overflowed." ein Beispiel für eine angemessene Meldungszeichenfolge.would be an appropriate message string.

Einschließen einer lokalisierten Meldungszeichenfolge in jede AusnahmeInclude a localized string message in every exception

Die dem Benutzer angezeigte Fehlermeldung wird von der Exception.Message-Eigenschaft der ausgelösten Ausnahme abgeleitet, nicht vom Namen der Ausnahmeklasse.The error message that the user sees is derived from the Exception.Message property of the exception that was thrown, and not from the name of the exception class. In der Regel können Sie der Exception.Message-Eigenschaft einen Wert zuweisen, indem Sie die Meldungszeichenfolge an das message-Argument eines Ausnahmekonstruktors übergeben.Typically, you assign a value to the Exception.Message property by passing the message string to the message argument of an Exception constructor.

Sie sollten für lokalisierte Anwendungen eine lokalisierte Meldungszeichenfolge für jede Ausnahme angeben, die Ihre Anwendung ausgeben könnte.For localized applications, you should provide a localized message string for every exception that your application can throw. Verwenden Sie Ressourcendateien, um lokalisierte Fehlermeldungen zur Verfügung zu stellen.You use resource files to provide localized error messages. Weitere Informationen zum Lokalisieren von Anwendungen und zum Abrufen lokalisierter Zeichenfolgen finden Sie unter Ressourcen in Desktop-Apps und System.Resources.ResourceManager.For information on localizing applications and retrieving localized strings, see Resources in Desktop Apps and System.Resources.ResourceManager.

Bereitstellen zusätzlicher Eigenschaften in benutzerdefinierten Ausnahmen, sofern erforderlichIn custom exceptions, provide additional properties as needed

Geben Sie zusätzliche Eigenschaften für eine Ausnahme nur dann neben der benutzerdefinierten Meldungszeichenfolge an, wenn es ein programmgesteuertes Szenario gibt, in dem ein solcher Zusatz sinnvoll ist.Provide additional properties for an exception (in addition to the custom message string) only when there's a programmatic scenario where the additional information is useful. Beispielsweise gibt die FileNotFoundException die FileName-Eigenschaft an.For example, the FileNotFoundException provides the FileName property.

Platzieren von throw-Anweisungen in einer Weise, dass die Stapelüberwachung nützlich istPlace throw statements so that the stack trace will be helpful

Die Stapelüberwachung beginnt bei der Anweisung, bei der die Ausnahme ausgelöst wurde, und endet mit der catch-Anweisung, mit der die Ausnahme abgefangen wird.The stack trace begins at the statement where the exception is thrown and ends at the catch statement that catches the exception.

Verwenden von Methoden zum Generieren von AusnahmenUse exception builder methods

Häufig löst eine Klasse die jeweils gleiche Ausnahme an unterschiedlichen Stellen in der Implementierung aus.It is common for a class to throw the same exception from different places in its implementation. Verwenden Sie Hilfsmethoden, die eine Ausnahme erstellen und zurückgeben, um ausufernden Code zu vermeiden.To avoid excessive code, use helper methods that create the exception and return it. Beispiel:For example:

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);
    }
};
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);
    }
}
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

In einigen Fällen ist es besser, eine Ausnahme mithilfe des zugehörigen Konstruktors zu erstellen.In some cases, it's more appropriate to use the exception's constructor to build the exception. Ein Beispiel hierfür ist eine globale Ausnahmeklasse wie etwa ArgumentException.An example is a global exception class such as ArgumentException.

Wiederherstellen des Status, wenn Methoden aufgrund von Ausnahmen nicht abgeschlossen werdenRestore state when methods don't complete due to exceptions

Aufrufende Funktionen sollten erwarten können, dass beim Auslösen einer Ausnahme durch eine Methode keine Nebeneffekte auftreten.Callers should be able to assume that there are no side effects when an exception is thrown from a method. Angenommen, Sie haben Code für Geldüberweisungen geschrieben. Ihr Code bucht einen Betrag von einem Konto ab und schreibt ihn einem anderen Konto gut. Wenn nun beim Ausführen der Gutschrift eine Ausnahme ausgelöst wird, darf die Abbuchung natürlich nicht bestehen bleiben.For example, if you have code that transfers money by withdrawing from one account and depositing in another account, and an exception is thrown while executing the deposit, you don't want the withdrawal to remain in effect.

public void TransferFunds(Account from, Account to, decimal amount)
{
    from.Withdrawal(amount);
    // If the deposit fails, the withdrawal shouldn't remain in effect.
    to.Deposit(amount);
}
Public Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
    from.Withdrawal(amount)
    ' If the deposit fails, the withdrawal shouldn't remain in effect.
    [to].Deposit(amount)
End Sub

Die obige Methode löst Ausnahmen nicht direkt aus, sondern muss defensiv geschrieben werden, damit, wenn die Gutschrift fehlschlägt, die Abbuchung rückgängig gemacht wird.The method above does not directly throw any exceptions, but must be written defensively so that if the deposit operation fails, the withdrawal is reversed.

In dieser Situation besteht eine Möglichkeit darin, alle Ausnahmen abzufangen, die von der Gutschrifttransaktion ausgelöst wurden, und für die Abbuchung einen Rollback auszuführen.One way to handle this situation is to catch any exceptions thrown by the deposit transaction and roll back the withdrawal.

private static void TransferFunds(Account from, Account to, decimal amount)
{
    string withdrawalTrxID = from.Withdrawal(amount);
    try
    {
        to.Deposit(amount);
    }
    catch
    {
        from.RollbackTransaction(withdrawalTrxID);
        throw;
    }
}
Private Shared Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
    Dim withdrawalTrxID As String = from.Withdrawal(amount)
    Try
        [to].Deposit(amount)
    Catch
        from.RollbackTransaction(withdrawalTrxID)
        Throw
    End Try
End Sub

Dieses Beispiel veranschaulicht die Verwendung von throw, um die ursprüngliche Ausnahme erneut auszulösen, damit aufrufende Funktionen die tatsächliche Ursache des Problems erkennen können, ohne die InnerException-Eigenschaft untersuchen zu müssen.This example illustrates the use of throw to re-throw the original exception, which can make it easier for callers to see the real cause of the problem without having to examine the InnerException property. Eine Alternative besteht darin, eine neue Ausnahme auszulösen und die ursprüngliche Ausnahme als innere Ausnahme einzuschließen:An alternative is to throw a new exception and include the original exception as the inner exception:

catch (Exception ex)
{
    from.RollbackTransaction(withdrawalTrxID);
    throw new TransferFundsException("Withdrawal failed.", innerException: ex)
    {
        From = from,
        To = to,
        Amount = amount
    };
}
Catch ex As Exception
    from.RollbackTransaction(withdrawalTrxID)
    Throw New TransferFundsException("Withdrawal failed.", innerException:=ex) With
    {
        .From = from,
        .[To] = [to],
        .Amount = amount
    }
End Try

Siehe auchSee also