Richtlinien für das Auslösen und Behandeln von Fehlern

Die folgenden Regeln dienen als Richtlinie für die Auslösung und Behandlung von Fehlern:

  • Alle Codepfade, die zu einer Ausnahme führen, sollten eine Methode bereitstellen, die auf erfolgreiche Ausführung prüft, ohne eine Ausnahme auszulösen. Um beispielsweise eine FileNotFoundException-Ausnahme zu vermeiden, können Sie File.Exists aufrufen. Dies ist vielleicht nicht immer möglich, Ziel ist jedoch, dass unter normalen Ausführungsbedigungen keine Ausnahme ausgelöst wird.

  • Schließen Sie Exception-Klassennamen mit dem Exception-Suffix ab, wie im folgenden Codebeispiel gezeigt.

    Public Class FileNotFoundException 
       Inherits Exception 
       ' Implementation code goes here.
    End Class
    [C#]
    public class FileNotFoundException : Exception 
    {
       // Implementation code goes here.
    }
    
  • Verwenden Sie beim Erstellen von Ausnahmeklassen die allgemeinen Konstruktoren, die im folgenden Codebeispiel enthalten sind.

    Public Class XxxException
       Inherits ApplicationException 
    
       Public Sub New()
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(message As String)
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(message As String, inner As Exception)
          ' Implementation code goes here.
       End Sub
    
       Public Sub New(info As SerializationInfo, context As StreamingContext)
          ' Implementation code goes here.
       End Sub
    End Class
    [C#]
    public class XxxException : ApplicationException 
    {
       public XxxException() {... }
       public XxxException(string message) {... }
       public XxxException(string message, Exception inner) {... }
       public XxxException(SerializationInfo info, StreamingContext context) {...}
    }
    
  • Für die meisten Fällen können Sie die vordefinierten Ausnahmetypen verwenden. Definieren Sie neue Ausnahmetypen nur dann für Programmszenarios, wenn vorgesehen ist, dass die Benutzer der Klassenbibliothek Ausnahmen dieses neuen Typs abfangen und eine programmgesteuerte Aktion ausführen, die auf dem Ausnahmetyp selbst basiert. Dies ersetzt die Analyse der Ausnahmezeichenfolge, die die Leistung und die Wartung beeinträchtigen würde.

    Es ist z. B. sinnvoll, eine FileNotFoundException zu definieren, da der Entwickler möglicherweise beschließt, die fehlende Datei zu erstellen. Andererseits ist eine FileIOException keine Ausnahme, die typischerweise eigens in Code behandelt werden würde.

  • Leiten Sie nicht alle neuen Ausnahmen direkt von der SystemException der Basisklasse ab. Vererben Sie die SystemException nur beim Erstellen neuer Ausnahmen in System-Namespaces. Vererben Sie beim Erstellen von neuen Ausnahmen in anderen Namespaces die ApplicationException.

  • Gruppieren Sie neue, aus SystemException oder ApplicationException abgeleitete Ausnahmen nach Namespace. Beispielsweise werden alle System.IO-Ausnahmen unter der IOException gruppiert (von SystemException abgeleitet), und alle Microsoft.Media-Ausnahmen könnten unter MediaException (von ApplicationException abgeleitet) gruppiert werden.

  • Verwenden Sie in jeder Ausnahme eine lokalisierte Beschreibungszeichenfolge. Wenn der Benutzer eine Fehlermeldung sieht, wird diese von der Beschreibungszeichenfolge der ausgelösten Ausnahme abgeleitet, und nicht von der Ausnahmeklasse.

  • Erstellen Sie grammatikalisch korrekte Fehlermeldungen mit korrekten Satzzeichen. Jeder Satz in der Beschreibungszeichenfolge für eine Ausnahme sollte mit einem Punkt enden. Code, der dem Benutzer eine allgemeine Ausnahmemeldung anzeigt, sollte nicht den Fall enthalten, dass ein Entwickler den Satzpunkt vergessen hat.

  • Stellen Sie Ausnahmeeigenschaften für den programmgesteuerten Zugriff bereit. Geben Sie Zusatzinformationen (abgesehen von der Beschreibungszeichenfolge) nur dann in einer Ausnahme an, wenn ein programmgesteuertes Szenario vorhanden ist, in dem diese Zusatzinformationen sinnvoll sind. Zusatzinformationen sollten nur in seltenen Fällen für eine Ausnahme erforderlich sein.

  • Legen Sie vertrauliche Informationen nicht in Ausnahmemeldungen offen. Als vertrauliche Informationen werden z. B. Pfade im lokalen Dateisystem behandelt. Durch bösartigen Code könnten anhand dieser Angaben private Benutzerinformationen auf dem Computer ermittelt werden.

  • Verwenden Sie keine Ausnahmen für normale oder erwartete Fehler oder für den normalen Steuerungsfluss.

  • Für sehr häufig vorkommende Fehler sollte Null zurückgegeben werden. Beispielsweise gibt ein File.Open-Befehl einen Null-Verweis zurück, wenn die Datei nicht gefunden wurde, jedoch eine Ausnahme, wenn die Datei gesperrt ist.

  • Entwerfen Sie Klassen so, dass bei normaler Verwendung niemals eine Ausnahme ausgelöst wird. Im folgenden Codebeispiel legt eine FileStream-Klasse eine weitere Möglichkeit der Bestimmung offen, ob das Ende der Datei erreicht wurde. Damit wird die Ausnahme umgangen, die ausgelöst wird, wenn der Entwickler über das Ende der Datei hinausliest.

    Class FileRead 
       Sub Open() 
          Dim stream As FileStream = File.Open("myfile.txt", FileMode.Open)
          Dim b As Byte
    
          ' ReadByte returns -1 at end of file.
          While b = stream.ReadByte() <> true
             ' Do something.
          End While
       End Sub
    End Class
    [C#]
    class FileRead 
    {
       void Open() 
       {
          FileStream stream = File.Open("myfile.txt", FileMode.Open);
          byte b;
    
          // ReadByte returns -1 at end of file.
          while ((b = stream.ReadByte()) != true) 
          {
             // Do something.
          }
       }
    }
    
  • Lösen Sie die InvalidOperationException-Ausnahme aus, wenn ein Aufruf eines set-Eigenschaftsaccessors oder einer entsprechenden Methode für den aktuellen Status des Objekts nicht geeignet ist.

  • Lösen Sie eine ArgumentException-Ausnahme aus, oder erstellen Sie eine von dieser Klasse abgeleitete Ausnahme, wenn ungültige Parameter übergeben oder entdeckt werden.

  • Denken Sie daran, dass die Stackablaufverfolgung an dem Punkt beginnt, an dem eine Ausnahme ausgelöst wird, und nicht an dem Punkt, an dem sie mit dem new-Operator erstellt wird. Berücksichtigen Sie dies, wenn Sie festlegen, wo eine Ausnahme ausgelöst werden soll.

  • 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. Um wiederkehrenden Code zu vermeiden, verwenden Sie Hilfsmethoden, die die Ausnahme mit Hilfe des new-Operators erstellen und zurückgeben. Das folgende Codebeispiel veranschaulicht, wie Sie eine Hilfsmethode implementieren.

    class File 
    {
       string fileName;  
       public byte[] Read(int bytes) 
       {
          if (!ReadFile(handle, bytes))
                throw NewFileIOException();
       }
    
       FileException NewFileIOException() 
       {
          string description = 
             // Build localized string, include fileName.
          return new FileException(description);
       }
    }
    
  • Lösen Sie Ausnahmen aus, anstatt einen Fehlercode oder HRESULT zurückzugeben.

  • Es sollte immer die präziseste Ausnahme ausgelöst werden.

  • Erstellen Sie aussagekräftige Meldungstexte für Ausnahmen, die dem Entwickler das Verständnis erleichtern.

  • Setzen Sie für die verwendete Ausnahme alle Felder.

  • Verwenden Sie innere Ausnahmen (verkettete Ausnahmen). Legen Sie jedoch nicht fest, dass Ausnahmen abgefangen und erneut ausgelöst werden, es sei denn, Sie fügen zusätzliche Informationen hinzu oder ändern den Typ der Ausnahme.

  • Erstellen Sie keine Methoden, die eine NullReferenceException-Ausnahme oder eine IndexOutOfRangeException-Ausnahme auslösen.

  • Führen Sie Argumentüberprüfung für geschützte (Family-) und interne (Assembly-) Member aus. Geben Sie in der Dokumentation eindeutig an, wenn die geschützte Methode keine Argumentüberprüfung ausführt. Wenn nicht anders angegeben, gehen Sie davon aus, dass die Argumentüberprüfung ausgeführt wird. Wird die Argumentüberprüfung jedoch nicht ausgeführt, kann dies zu einer Leistungssteigerung führen.

  • Bereinigen Sie etwaige Nebeneffekte, die beim Auslösen einer Ausnahme entstehen. Der Aufrufer sollte davon ausgehen können, dass keine Nebeneffekte auftreten, wenn eine Ausnahme von einer Funktion ausgelöst wird. Wenn beispielsweise eine Hashtable.Insert-Methode eine Ausnahme auslöst, kann der Aufrufer davon ausgehen, dass das angegebene Element nicht zur Hashtable hinzugefügt worden war.

Standardausnahmetypen

In der folgenden Tabelle werden die Standardausnahmen aufgelistet, die in der Runtime bereitgestellt werden. Außerdem werden die Bedingungen genannt, für die Sie eine abgeleitete Klasse erstellen sollten.

Ausnahmetyp Basistyp Beschreibung Beispiel
Exception Object Basisklasse für alle Ausnahmen. Keines (verwenden Sie eine abgeleitete Klasse dieser Ausnahme).
SystemException Exception Basisklasse für alle von der Runtime generierten Fehler. Keines (verwenden Sie eine abgeleitete Klasse dieser Ausnahme).
IndexOutOfRangeException SystemException Wird von der Runtime nur dann ausgelöst, wenn ein Array falsch indiziert wurde. Indizieren eines Arrays außerhalb seines gültigen Bereichs:

arr[arr.Length+1]

NullReferenceException SystemException Wird von der Runtime nur dann ausgelöst, wenn auf ein Null-Objekt verwiesen wurde. object o = null;

o.ToString();

InvalidOperationException SystemException Wird von Methoden ausgelöst, wenn ein ungültiger Status vorliegt. Aufrufen von Enumerator.GetNext() nach Entfernen eines Item aus der zugrunde liegenden Auflistung.
ArgumentException SystemException Basisklasse für alle Argumentausnahmen. Keines (verwenden Sie eine abgeleitete Klasse dieser Ausnahme).
ArgumentNullException ArgumentException Wird von Methoden ausgelöst, bei denen ein Argument nicht Null sein darf. String s = null;

"Calculate".IndexOf (s);

ArgumentOutOfRangeException ArgumentException Wird von Methoden ausgelöst, die überprüfen, ob Argumente innerhalb eines gegebenen Bereichs liegen. String s = "string";

s.Chars[9];

ExternalException SystemException Basisklasse für Ausnahmen, die in Umgebungen außerhalb der Runtime auftreten oder für diese vorgesehen sind. Keines (verwenden Sie eine abgeleitete Klasse dieser Ausnahme).
COMException ExternalException Ausnahme, die COM-Hresult-Informationen einkapselt. Wird in COM-Interop verwendet.
SEHException ExternalException Ausnahme, die Informationen für strukturierte Ausnahmebehandlung in Win32 einkapselt. Wird bei der Interoperabilität von nicht-verwaltetem Code verwendet.

Umbrechen von Ausnahmen

Fehler, die auf der gleichen Ebene auftreten wie eine Komponente, sollten eine Ausnahme auslösen, die für die Zielgruppe leicht verständlich ist. Im folgenden Codebeispiel richtet sich die Fehlermeldung an Benutzer der TextReader-Klasse, die versucht, aus einem Stream zu lesen.

Public Class TextReader   
   Public Function ReadLine() As String
      Try
         ' Read a line from the stream.
      Catch e As Exception
         Throw New IOException("Could not read from stream", e)
      End Try
   End Function 
End Class 
[C#]
public class TextReader
{
   public string ReadLine()
   {
      try
      {
         // Read a line from the stream.
      } 
      catch (Exception e)
      {
         throw new IOException ("Could not read from stream", e);
      }
   }
}

Siehe auch

Entwurfsrichtlinien für die Entwicklung von Klassenbibliotheken | Behandeln und Auslösen von Ausnahmen