Empfohlene Vorgehensweise für die Ausnahmebehandlung

Aktualisiert: November 2007

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

    Im folgenden Beispiel wird eine Ausnahme ausgelöst, wenn die Verbindung nicht geschlossen ist.

       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
    
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    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 EmployeeListNotFoundException
        Inherits Exception
    
    public class MyFileNotFoundException : 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 A die Ausnahme richtig abfangen und behandeln kann, muss A die Assembly finden, in der die von B ausgelöste Ausnahme enthalten ist. Wenn B eine Ausnahme auslöst, die in einer Assembly unter ihrer Anwendungsbasis enthalten ist, aber nicht unter der Anwendungsbasis von A, kann A die Ausnahme nicht finden. Daraufhin wird von der CLR die 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 Opennull 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 Open(ByVal fileToRead As FileStream)
    
            ' This If statement is optional
            ' as it is very unlikely that
            ' the stream would ever be null
            If IsDBNull(fileToRead) 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
                b = fileToRead.ReadByte()
                Console.Write(b.ToString())
                ' Or do something else with the byte.
            Next
        End Sub
    
    End Class
    class FileRead {
        public void Open(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.
            }
        }
    } 
    
  • 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 File
       Private fileName As String
    
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
    
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(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.

Siehe auch

Weitere Ressourcen

Behandeln und Auslösen von Ausnahmen