Freigeben über


Erstellen und Auslösen von Ausnahmen

Ausnahmen werden verwendet, um anzugeben, dass beim Ausführen des Programms ein Fehler aufgetreten ist. Ausnahmeobjekte, die einen Fehler beschreiben, werden erstellt und dann per throw. Die Laufzeit sucht dann nach dem kompatibelsten Ausnahmehandler.

Programmierer sollten Ausnahmen auslösen, wenn mindestens eine der folgenden Bedingungen zutrifft:

  • Die Methode kann ihre definierte Funktionalität nicht abschließen. Wenn beispielsweise ein Parameter für eine Methode einen ungültigen Wert aufweist:

    static void CopyObject(SampleClass original)
    {
        _ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original));
    }
    
  • Ein unangemessener Aufruf eines Objekts erfolgt basierend auf dem Objektzustand. Ein Beispiel ist möglicherweise der Versuch, in eine schreibgeschützte Datei zu schreiben. In Fällen, in denen ein Objektstatus einen Vorgang nicht zulässt, wird eine Instanz von InvalidOperationException oder ein Objekt basierend auf einer Ableitung dieser Klasse ausgelöst. Der folgende Code ist ein Beispiel für eine Methode, die ein InvalidOperationException-Objekt auslöst:

    public class ProgramLog
    {
        FileStream logFile = null!;
        public void OpenLog(FileInfo fileName, FileMode mode) { }
    
        public void WriteLog()
        {
            if (!logFile.CanWrite)
            {
                throw new InvalidOperationException("Logfile cannot be read-only");
            }
            // Else write data to the log and return.
        }
    }
    
  • Wenn ein Argument für eine Methode eine Ausnahme verursacht. In diesem Fall sollte die ursprüngliche Ausnahme abgefangen und eine ArgumentException-Instanz erstellt werden. Die ursprüngliche Ausnahme sollte als ArgumentException-Parameter an den Konstruktor der InnerException übergeben werden:

    static int GetValueFromArray(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    

    Anmerkung

    Im vorherigen Beispiel wird gezeigt, wie die InnerException-Eigenschaft verwendet wird. Es ist absichtlich vereinfacht. Bevor Sie einen Index verwenden, sollten Sie in der Praxis überprüfen, ob er im Bereich liegt. Sie können diese Technik verwenden, um eine Ausnahme umzuschließen, wenn ein Element eines Parameters eine Ausnahme auslöst, die Sie vor dem Aufrufen des Elements nicht antizipieren konnten.

Ausnahmen enthalten eine Eigenschaft mit dem Namen StackTrace. Diese Zeichenfolge enthält den Namen der Methoden im aktuellen Aufrufstapel zusammen mit dem Dateinamen und der Zeilennummer, unter der die Ausnahme für jede Methode ausgelöst wurde. Ein StackTrace-Objekt wird automatisch von der Common Language Runtime (CLR) ab dem Punkt der throw-Anweisung erstellt, sodass Ausnahmen an dem Punkt ausgelöst werden müssen, an dem die Stapelverfolgung beginnen soll.

Alle Ausnahmen enthalten eine Eigenschaft mit dem Namen Message. Diese Zeichenfolge sollte so festgelegt werden, dass der Grund für die Ausnahme erläutert wird. Informationen, die für die Sicherheit vertraulich sind, sollten nicht in den Nachrichtentext gesetzt werden. Zusätzlich zu Messageenthält ArgumentException eine Eigenschaft mit dem Namen ParamName, die auf den Namen des Arguments festgelegt werden soll, das dazu führte, dass die Ausnahme ausgelöst wurde. In einem Eigenschaftensatzer sollte ParamName auf valuefestgelegt werden.

Öffentliche und geschützte Methoden lösen Ausnahmen aus, wenn sie ihre beabsichtigten Funktionen nicht abschließen können. Die ausgelöste Ausnahmeklasse ist die spezifischste Ausnahme, die den Fehlerbedingungen entspricht. Diese Ausnahmen sollten als Teil der Klassenfunktionalität dokumentiert werden, und abgeleitete Klassen oder Aktualisierungen der ursprünglichen Klasse sollten das gleiche Verhalten für Die Abwärtskompatibilität beibehalten.

Dinge, die beim Auslösen von Ausnahmen vermieden werden müssen

In der folgenden Liste werden Methoden aufgeführt, die beim Auslösen von Ausnahmen vermieden werden sollen:

  • Verwenden Sie keine Ausnahmen, um den Fluss eines Programms als Teil der normalen Ausführung zu ändern. Verwenden Sie Ausnahmen zum Melden und Behandeln von Fehlerbedingungen.
  • Ausnahmen sollten nicht als Rückgabewert oder Parameter zurückgegeben werden, sie sollten ausgelöst werden.
  • Lösen Sie nicht System.Exception, System.SystemException, System.NullReferenceExceptionoder System.IndexOutOfRangeException absichtlich aus Ihrem eigenen Quellcode aus.
  • Erstellen Sie keine Ausnahmen, die im Debugmodus ausgelöst werden können, jedoch nicht im Freigabemodus. Um Laufzeitfehler während der Entwicklungsphase zu identifizieren, verwenden Sie stattdessen Debug Assert.

Ausnahmen in Aufgabenrückgabemethoden

Methoden, die mit dem async Modifizierer deklariert werden, weisen einige besondere Überlegungen auf, wenn es um Ausnahmen geht. Ausnahmen, die in einer async-Methode ausgelöst werden, werden in der zurückgegebenen Aufgabe gespeichert und treten erst auf, wenn beispielsweise auf die Aufgabe gewartet wird. Weitere Informationen zu gespeicherten Ausnahmen finden Sie unter Asynchrone Ausnahmen.

Es wird empfohlen, Argumente zu überprüfen und entsprechende Ausnahmen wie ArgumentException und ArgumentNullExceptionauszugeben, bevor Sie die asynchronen Teile Ihrer Methoden eingeben. Das heißt, diese Überprüfungsausnahmeregelungen sollten synchron auftreten, bevor die Arbeit beginnt. Der folgende Codeausschnitt zeigt ein Beispiel, bei dem, wenn die Ausnahmen geworfen werden, die ArgumentException-Ausnahmen synchron auftreten würden, während die InvalidOperationException-Ausnahmen in der zurückgegebenen Task gespeichert würden.

// Non-async, task-returning method.
// Within this method (but outside of the local function),
// any thrown exceptions emerge synchronously.
public static Task<Toast> ToastBreadAsync(int slices, int toastTime)
{
    if (slices is < 1 or > 4)
    {
        throw new ArgumentException(
            "You must specify between 1 and 4 slices of bread.",
            nameof(slices));
    }

    if (toastTime < 1)
    {
        throw new ArgumentException(
            "Toast time is too short.", nameof(toastTime));
    }

    return ToastBreadAsyncCore(slices, toastTime);

    // Local async function.
    // Within this function, any thrown exceptions are stored in the task.
    static async Task<Toast> ToastBreadAsyncCore(int slices, int time)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        // Start toasting.
        await Task.Delay(time);

        if (time > 2_000)
        {
            throw new InvalidOperationException("The toaster is on fire!");
        }

        Console.WriteLine("Toast is ready!");

        return new Toast();
    }
}

Definieren von Ausnahmeklassen

Programme können entweder eine vordefinierte Ausnahmeklasse im System-Namespace werfen (ausgenommen, wo zuvor angegeben) oder eigene Ausnahmeklassen erstellen, indem sie von Exceptionableiten. Die abgeleiteten Klassen sollten mindestens drei Konstruktoren definieren: einen parameterlosen Konstruktor, einen, der die Nachrichteneigenschaft festlegt, und eine, die sowohl die eigenschaften Message als auch InnerException festlegt. Zum Beispiel:

[Serializable]
public class InvalidDepartmentException : Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
}

Fügen Sie der Ausnahmeklasse neue Eigenschaften hinzu, wenn die bereitgestellten Daten nützlich sind, um die Ausnahme aufzulösen. Wenn der abgeleiteten Ausnahmeklasse neue Eigenschaften hinzugefügt werden, sollten ToString() überschrieben werden, um die hinzugefügten Informationen zurückzugeben.

C#-Sprachspezifikation

Weitere Informationen finden Sie unter Ausnahmen und Die throw-Anweisung in der C#-Sprachspezifikation. Die Sprachspezifikation ist die endgültige Quelle für C#-Syntax und -Verwendung.

Siehe auch