Übung: Abfangen bestimmter Ausnahmetypen

Abgeschlossen

Weiter oben in diesem Modul haben Sie gelernt, dass die von Ihrer C#-Anwendung erfassten Ausnahmeobjekte Instanzen einer Ausnahmeklasse sind. Im Allgemeinen fängt Ihr Code (mit catch) eines der folgenden Objekte ab:

  • Ein Ausnahmeobjekt, das eine Instanz der System.Exception Basisklasse ist.
  • Ein Ausnahmeobjekt, das eine Instanz eines Ausnahmetyps ist, der von der Basisklasse erbt. Beispiel: eine Instanz der InvalidCastException Klasse.

Untersuchen von Ausnahmeeigenschaften

System.Exception ist die Basisklasse, von der alle abgeleiteten Ausnahmetypen erben. Jeder Ausnahmetyp erbt von der Basisklasse über eine bestimmte Klassenhierarchie. Die Klassenhierarchie für das InvalidCastException Beispiel lautet wie folgt:

Object
    Exception
        SystemException
            InvalidCastException

Die meisten Ausnahmeklassen, die von Exception erben, fügen keine zusätzlichen Funktionen hinzu; sie erben einfach von Exception. Daher können Sie die Eigenschaften der Exception Klasse untersuchen, um die meisten Ausnahmen zu verstehen und wie Sie eine Ausnahme in Ihrem Code verwenden können.

Dies sind die Eigenschaften der Exception Klasse:

  • Daten: Die Data Eigenschaft enthält beliebige Daten in Schlüsselwertpaaren.
  • HelpLink: Die HelpLink Eigenschaft kann verwendet werden, um eine URL (oder URN) für eine Hilfedatei zu speichern, die umfangreiche Informationen zur Ursache einer Ausnahme bereitstellt.
  • HResult: Die HResult Eigenschaft kann verwendet werden, um auf einen codierten numerischen Wert zuzugreifen, der einer bestimmten Ausnahme zugewiesen ist.
  • InnerException: Die InnerException Eigenschaft kann verwendet werden, um eine Reihe von Ausnahmen während der Ausnahmebehandlung zu erstellen und beizubehalten.
  • Meldung: Die Message Eigenschaft enthält Details zur Ursache einer Ausnahme.
  • Quelle: Die Source Eigenschaft kann verwendet werden, um auf den Namen der Anwendung oder das Objekt zuzugreifen, das den Fehler verursacht.
  • StackTrace: Die StackTrace Eigenschaft enthält eine Stapelablaufverfolgung, die verwendet werden kann, um zu bestimmen, wo ein Fehler aufgetreten ist.
  • TargetSite: Die TargetSite Eigenschaft kann verwendet werden, um die Methode abzurufen, die die aktuelle Ausnahme auslöst.

Es ist in Ordnung, wenn Sie sich durch diese Untersuchung von Ausnahmeeigenschaften, Basisklassen und Vererbung etwas überwältigt fühlen. Machen Sie sich keine Sorgen, Ausnahmen in Ihrem Code abzufangen und auf die Eigenschaften einer Ausnahme zuzugreifen ist einfacher als zu erklären, wie Ausnahmen und ihre Eigenschaften funktionieren.

Hinweis

In diesem Modul konzentrieren Sie sich auf die Verwendung der Nachrichteneigenschaft einer Ausnahme, um die Ausnahme in der Benutzeroberfläche Ihrer Anwendung zu melden.

Zugreifen auf die Eigenschaften eines Ausnahmeobjekts

Nachdem Sie nun mit Ausnahmeobjekten und deren Eigenschaften vertraut sind, ist es an der Zeit, mit dem Codieren zu beginnen.

  1. Aktualisieren Sie Ihre Program.cs Datei wie folgt:

    try
    {
        Process1();
    }
    catch
    {
        Console.WriteLine("An exception has occurred");
    }
    
    Console.WriteLine("Exit program");
    
    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch
        {
            Console.WriteLine("Exception caught in Process1");
        }
    }
    
    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
    
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
    }
    
  2. Überprüfen Sie kurz den Code.

    Dies ist derselbe Code, den Sie in der vorherigen Einheit (der Lösungscode für die Herausforderungsaktivität) gesehen haben. Sie wissen, dass während der WriteMessage Methode eine Ausnahme ausgelöst wird. Sie wissen auch, dass die Ausnahme in der Process1 Methode abgefangen wird. Sie verwenden diesen Code, um Ausnahmeobjekte und bestimmte Ausnahmetypen zu untersuchen.

  3. Aktualisieren Sie die Process1-Methode wie folgt:

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  4. Nehmen Sie sich eine Minute Zeit, um Ihre Aktualisierungen zu untersuchen.

    Beachten Sie, dass ihre aktualisierte catch Klausel eine Instanz der Exception Klasse in einem Objekt namens exabfangen wird. Beachten Sie außerdem, dass Ihre Console.WriteLine()-Methode ex verwendet, um auf die Eigenschaft des Message-Objekts zuzugreifen und die Fehlermeldung in der Konsole anzuzeigen.

    Obwohl die catch Klausel ohne Argumente verwendet werden kann, wird dieser Ansatz nicht empfohlen. Wenn Sie kein Argument angeben, werden alle Ausnahmetypen abgefangen und Sie können nicht zwischen ihnen unterscheiden.

    Im Allgemeinen sollten Sie nur die Ausnahmen abfangen, die durch Ihren Code behandelt werden können. Daher sollte Ihre catch Klausel ein Objektargument angeben, das von System.Exception abgeleitet ist. Der Ausnahmetyp sollte so spezifisch wie möglich sein. Dies hilft, Ausnahmen zu vermeiden, die vom Ausnahmehandler nicht aufgelöst werden können. Sie aktualisieren Ihren Code, um einen bestimmten Ausnahmetyp später in dieser Übung abzufangen.

  5. Klicken Sie im Menü File (Datei) auf Save (Speichern).

  6. Legen Sie den Haltepunkt in der folgenden Codezeile fest:

    Console.WriteLine($"Exception caught in Process1: {ex.Message}");
    
  7. Wählen Sie im Menü "Ausführen" die Option "Debuggen starten" aus.

    Die Codeausführung sollte am Haltepunkt angehalten werden.

  8. Bewegen Sie den Mauszeiger über ex.

    Beachten Sie, dass IntelliSense die gleichen Ausnahmeeigenschaften anzeigt, die Sie zuvor untersucht haben.

  9. Nehmen Sie sich eine Minute Zeit, um die Informationen zu untersuchen, die das Ausnahmeobjekt exbeschreiben.

    Beachten Sie, dass es sich um eine Ausnahme des Typs System.DivideByZeroException handelt, und dass die Eigenschaft Message auf Attempted to divide by zero. festgelegt ist.

    Beachten Sie, dass die StackTrace Eigenschaft die Methode und die Zeilennummer meldet, an der der Fehler aufgetreten ist, sowie die Abfolge der Methodenaufrufe (und Zeilennummern), die zum Fehler geführt haben.

  10. Wählen Sie auf der Symbolleiste "Debuggen" die Option "Weiter" aus.

  11. Nehmen Sie sich eine Minute Zeit, um die Konsolenausgabe zu untersuchen.

    Beachten Sie, dass die Eigenschaft der Message Ausnahme in der ausgabe enthalten ist, die von Ihrer Anwendung generiert wird:

    ∞
    Exception caught in Process1: Attempted to divide by zero.
    Exit program
    

Abfangen eines bestimmten Ausnahmetyps

Nachdem Sie nun den Typ der abzufangenden Ausnahme kennen, können Sie Ihre catch Klausel aktualisieren, um diesen bestimmten Ausnahmetyp zu behandeln.

  1. Aktualisieren Sie die Process1-Methode wie folgt:

    static void Process1()
    {
        try
        {
            WriteMessage();
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"Exception caught in Process1: {ex.Message}");
        }
    }
    
  2. Speichern Sie Ihren Code, und starten Sie dann eine Debugsitzung.

  3. Beachten Sie, dass ihre aktualisierte Anwendung die gleichen Nachrichten an der Konsole meldet.

    Obwohl die gemeldeten Nachrichten identisch sind, gibt es einen wichtigen Unterschied. Ihre Process1 Methode erfasst nur Ausnahmen des spezifischen Typs, den sie verarbeiten kann.

  4. Um einen anderen Ausnahmetyp zu generieren, aktualisieren Sie die WriteMessage Methode wie folgt:

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        Console.WriteLine(float1 / float2);
        // Console.WriteLine(number1 / number2);
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    
  5. Beachten Sie die Verwendung der checked-Anweisung.

    Bei der Durchführung von Integraltypberechnungen, die den Wert eines integralen Typs einem anderen integralen Typ zuweisen, hängt das Ergebnis vom Überlaufüberprüfungskontext ab. In einem checked Kontext ist die Konvertierung erfolgreich, wenn sich der Quellwert innerhalb des Bereichs des Zieltyps befindet. Andernfalls wird eine OverflowException ausgelöst. In einem deaktivierten Kontext ist die Konvertierung immer erfolgreich und wird wie folgt fortgesetzt:

    • Wenn der Quelltyp größer als der Zieltyp ist, wird der Quellwert abgeschnitten, indem die wichtigsten "zusätzlichen" Bits verworfen werden. Das Ergebnis wird dann als Wert des Zieltyps behandelt.

    • Wenn der Quelltyp kleiner als der Zieltyp ist, ist der Quellwert entweder mit einem Vorzeichen oder einer Null erweitert, sodass er dieselbe Größe wie der Zieltyp aufweist. Vorzeichenerweiterung wird verwendet, wenn der Quelltyp ein Vorzeichen hat; Nullerweiterung wird verwendet, wenn der Quelltyp kein Vorzeichen hat. Das Ergebnis wird dann als Wert des Zieltyps behandelt.

    • Wenn der Quelltyp die gleiche Größe wie der Zieltyp aufweist, wird der Quellwert als Wert des Zieltyps behandelt.

    Hinweis

    Integrale Typberechnungen, die sich nicht innerhalb eines checked Codeblocks befinden, werden so behandelt, als ob sie sich in einem unchecked Codeblock befinden.

  6. Speichern Sie Ihren Code, und starten Sie dann eine Debugsitzung.

  7. Beachten Sie, dass ein neuer Ausnahmetyp nicht innerhalb der catch-Methode, sondern durch die Process1-Klausel in den Anweisungen der obersten Ebene abgefangen wird.

    Ihre Anwendung druckt die folgenden Meldungen in der Konsole:

    ∞
    An exception has occurred
    Exit program
    

    Hinweis

    Der catch Block in Process1 wird nicht ausgeführt. Dies ist das gewünschte Verhalten. Fangen Sie nur die Ausnahmen ab, auf deren Behandlung Ihr Code vorbereitet ist.

Abfangen mehrerer Ausnahmen in einem Codeblock

An diesem Punkt fragen Sie sich vielleicht, was passiert, wenn mehrere Ausnahmen in einem einzigen Codeblock auftreten. Fängt Ihr Code (über catch) jede Ausnahme bei deren Auftreten ab?

  1. Aktualisieren Sie die WriteMessage-Methode wie folgt:

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        Console.WriteLine(float1 / float2);
        Console.WriteLine(number1 / number2);
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    
  2. Platzieren Sie einen Haltepunkt innerhalb der WriteMessage()-Methode in der folgenden Codezeile:

    Console.WriteLine(float1 / float2);
    
  3. Speichern Sie Ihren Code, und starten Sie dann eine Debugsitzung.

  4. Durchlaufen Sie Schritt für Schritt Ihren Code, Zeile für Zeile, und beachten Sie dann, was passiert, nachdem der Code den ersten Ausnahmefall behandelt hat.

    Wenn die erste Ausnahme auftritt, wird das Steuerelement an die erste catch Klausel übergeben, die die Ausnahme behandeln kann. Der Code, der die zweite Ausnahme generiert, wird nie erreicht. Dies bedeutet, dass ein Teil des Codes nie ausgeführt wird. Dies könnte zu schwerwiegenden Problemen führen.

  5. Nehmen Sie sich eine Minute Zeit, um zu überlegen, wie Sie mehrere Ausnahmen verwalten können, und wann/warum Der Code möglicherweise nicht mehrere Ausnahmen verwalten soll.

    Sie haben an früherer Stelle in diesem Modul gelernt, dass Ausnahmen möglichst nahe an dem Ort abgefangen werden sollten, an dem sie auftreten. Unter Berücksichtigung dieser Richtlinie können Sie die WriteMessage-Methode aktualisieren, um Ausnahmen mit einer eigenen try-catch-Methode abzufangen. Beispiel:

    static void WriteMessage()
    {
        double float1 = 3000.0;
        double float2 = 0.0;
        int number1 = 3000;
        int number2 = 0;
        byte smallNumber;
    
        try
        {
            Console.WriteLine(float1 / float2);
            Console.WriteLine(number1 / number2);
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}");
        }
        checked
        {
            smallNumber = (byte)number1;
        }
    }
    

    Sie können auch den Code, der die OverflowException verursacht, in einer separaten try-catch innerhalb der WriteMessage()-Methode umschließen.

    checked
    {
        try
        {
            smallNumber = (byte)number1;
        }
        catch (OverflowException ex)
        {
            Console.WriteLine($"Exception caught in WriteMessage: {ex.Message}");
        }  
    }
    
  6. Unter welchen Bedingungen wäre es unerwünscht, nachfolgende Ausnahmen abzufangen?

    Berücksichtigen Sie den Fall, wenn die Methode (oder der Codeblock) einen zweiteiligen Prozess abschließt. Gehen Sie davon aus, dass der zweite Teil des Prozesses vom abschluss des ersten Teils abhängig ist. Wenn der erste Teil des Prozesses nicht erfolgreich abgeschlossen werden kann, gibt es keinen Grund, den zweiten Teil des Prozesses fortzusetzen. In diesem Fall ist es häufig besser, dem Benutzer eine Meldung zu präsentieren, in der die Fehlerbedingung erläutert wird, ohne die verbleibenden Teile oder Teile des größeren Prozesses zu versuchen.

Abfangen verschiedener Ausnahmetypen in einem Codeblock

Es gibt Zeiten, in denen Variationen in Ihren Daten verschiedene Arten von Ausnahmen verursachen können.

  1. Löschen Sie Ihre Haltepunkte, und ersetzen Sie dann den Inhalt der „Datei Program.cs“ durch den folgenden Code:

    // inputValues is used to store numeric values entered by a user
    string[] inputValues = new string[]{"three", "9999999999", "0", "2" };
    
    foreach (string inputValue in inputValues)
    {
        int numValue = 0;
        try
        {
            numValue = int.Parse(inputValue);
        }
        catch (FormatException)
        {
            Console.WriteLine("Invalid readResult. Please enter a valid number.");
        }
        catch (OverflowException)
        {
            Console.WriteLine("The number you entered is too large or too small.");
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    
  2. Nehmen Sie sich eine Minute Zeit, um diesen Code zu überprüfen.

    Zunächst erstellt der Code ein Zeichenfolgenarray mit dem Namen inputValues. Die Daten im Array sollen die Eingabewerte darstellen, die von einem Benutzer eingegeben wurden, der angewiesen wurde, numerische Werte einzugeben. Je nach eingegebenen Wert können unterschiedliche Ausnahmetypen auftreten.

    Beachten Sie, dass der Code die int.Parse Methode verwendet, um die Zeichenfolgenwerte "eingabe" in ganze Zahlen zu konvertieren. Der int.Parse Code wird in einem try Codeblock platziert.

  3. Legen Sie einen Haltepunkt in der folgenden Codezeile fest:

    int numValue = 0;
    
  4. Speichern Sie Ihren Code, und starten Sie dann eine Debugsitzung.

  5. Gehen Sie den Code Zeile für Zeile durch, und beachten Sie, dass verschiedene Ausnahmetypen abgefangen werden.

Zusammenfassung

Nachstehend finden Sie nochmals die wichtigsten Punkte aus dieser Lerneinheit:

  • Die catch Klausel sollte so konfiguriert werden, dass ein bestimmter Ausnahmetyp erfasst wird. Beispielsweise der DivideByZeroException Ausnahmetyp.
  • Auf die Eigenschaften eines Ausnahmeobjekts kann innerhalb des catch Blocks zugegriffen werden. Sie können beispielsweise die Message Eigenschaft verwenden, um den Anwendungsbenutzer über ein Problem zu informieren.
  • Sie können zwei oder mehr catch Klauseln angeben, wenn Sie mehrere Ausnahmetypen erfassen müssen.