Die Verwendung von try catch für die Ausnahmebehandlung ist eine bewährte Methode


201

Während ich den Code meines Kollegen selbst von jemandem pflege, der behauptet, ein leitender Entwickler zu sein, sehe ich oft den folgenden Code:

try
{
  //do something
}
catch
{
  //Do nothing
}

oder manchmal schreiben sie Protokollinformationen in Protokolldateien wie den folgenden try catchBlock

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

Ich frage mich nur, ob das, was sie getan haben, die beste Praxis ist. Es macht mich verwirrt, weil Benutzer in meinem Denken wissen sollten, was mit dem System passiert.

Bitte geben Sie mir einen Rat.


128
Snippet Nr. 1 ist in 99,999% der Fälle nicht akzeptabel.
Leppie

22
Das Anzeigen von Ausnahmen direkt für den Benutzer ist vor allem aus zwei Gründen keine gute Idee: 1. Wenn es sich um normale Benutzer handelt, ärgert er sich über das Lesen einer Fehlermeldung, die nur sehr wenige Informationen für ihn / sie enthält. 2. Wenn er ein sogenannter Hacker ist, kann er nützliche Informationen erhalten. Die beste Vorgehensweise, IMO, besteht darin, Ausnahmen zu protokollieren und eine freundliche Fehlermeldung anzuzeigen.
Leri

4
@leppie Wenn etwas Unerwartetes auftritt (wie NullReferenceoder ArgumentNulldas nicht Teil des Anwendungsflusses ist), bedeutet dies, dass ein Fehler behoben werden muss, sodass die Protokollierung dazu beiträgt, Ihren Code viel schneller zu debuggen.
Leri

14
Die Verwendung eines Try-Catch-Blocks zum Ausblenden einer Ausnahme ist im Allgemeinen das Ergebnis einer verzögerten Programmierung. Es ist eine Verknüpfung, die häufig verwendet wird, anstatt Validierungscode zum Testen von Eingaben zu schreiben. Sehr gelegentlich kann es vorkommen, dass eine Ausnahme auftritt, die den Betrieb Ihres Codes nicht beeinträchtigt, und das Ausblenden auf diese Weise ist möglicherweise in Ordnung. Dies ist jedoch ziemlich selten.
Corey

12
@Toan, na ja, wenn es sich um einen Batch-Job handelt, fange ich auf der obersten Ebene (Main) an, um zu protokollieren, und wirf dann erneut, um einen Alarm auszulösen, dass der Job abnormal beendet wurde. Wenn es sich um eine Web-App handelt, lasse ich die Ausnahme zu einem globalen Handler sprudeln, protokolliere und leite den Benutzer dann zu einem Fehlerbildschirm um. Ihr Anwendungsszenario bestimmt, was Sie mit dieser Ausnahme tun, nachdem Sie sie protokolliert oder anderweitig behandelt haben.
Anthony Pegram

Antworten:


300

Meine Strategie zur Ausnahmebehandlung lautet:

  • Um alle unbehandelten Ausnahmen abzufangen, indem Sie sich an die Application.ThreadException eventanschließen, entscheiden Sie:

    • Für eine UI-Anwendung: um sie dem Benutzer mit einer Entschuldigungsnachricht (Winforms) anzuzeigen.
    • Für einen Dienst oder eine Konsolenanwendung: Protokollieren Sie sie in einer Datei (Dienst oder Konsole).

Dann füge ich immer jeden Code hinzu, der extern ausgeführt wird in try/catch:

  • Alle von der Winforms-Infrastruktur ausgelösten Ereignisse (Laden, Klicken, SelectedChanged ...)
  • Alle Ereignisse, die von Komponenten Dritter ausgelöst werden

Dann füge ich 'try / catch' bei

  • Alle mir bekannten Operationen funktionieren möglicherweise nicht immer (E / A-Operationen, Berechnungen mit einer potenziellen Nullteilung ...). In einem solchen Fall werfe ich einen neuen ApplicationException("custom message", innerException), um zu verfolgen, was wirklich passiert ist

Außerdem versuche ich mein Bestes, um Ausnahmen richtig zu sortieren . Es gibt Ausnahmen, die:

  • müssen dem Benutzer sofort angezeigt werden
  • erfordern einige zusätzliche Verarbeitung, um Dinge zusammenzusetzen, wenn sie auftreten, um Kaskadenprobleme zu vermeiden (dh: Fügen Sie .EndUpdate finallywährend eines Füllvorgangs in den Abschnitt ein TreeView).
  • Dem Benutzer ist das egal, aber es ist wichtig zu wissen, was passiert ist. Also logge ich sie immer ein:

    • Im Ereignisprotokoll
    • oder in einer .log-Datei auf der Festplatte

Es wird empfohlen, einige statische Methoden zu entwerfen, um Ausnahmen in den Fehlerbehandlungsroutinen der obersten Ebene der Anwendung zu behandeln .

Ich zwinge mich auch zu versuchen:

  • Denken Sie daran, dass ALLE Ausnahmen bis zur obersten Ebene gesprudelt sind . Es ist nicht erforderlich, überall Ausnahmebehandlungsroutinen zu platzieren.
  • Wiederverwendbare oder tief aufgerufene Funktionen müssen keine Ausnahmen anzeigen oder protokollieren: Sie werden entweder automatisch gesprudelt oder mit einigen benutzerdefinierten Nachrichten in meinen Ausnahmebehandlungsroutinen erneut ausgelöst.

So endlich :

Schlecht:

// DON'T DO THIS, ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

Nutzlos:

// DONT'T DO THIS, ITS USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

Endlich einen Versuch ohne Fang zu machen, ist absolut gültig:

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT
    listView1.EndUpdate();
}

Was ich auf höchster Ebene mache:

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --

    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

Was ich in einigen sogenannten Funktionen mache:

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

Es gibt viel mit der Ausnahmebehandlung (benutzerdefinierte Ausnahmen) zu tun, aber diese Regeln, die ich zu beachten versuche, reichen für die einfachen Anwendungen aus, die ich mache.

Hier ist ein Beispiel für Erweiterungsmethoden, mit denen abgefangene Ausnahmen auf komfortable Weise behandelt werden können. Sie sind so implementiert, dass sie miteinander verkettet werden können, und es ist sehr einfach, eine eigene Verarbeitung abgefangener Ausnahmen hinzuzufügen.

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}

98
catch(Exception ex) { throw ex; }in C # ist schlechter als redundant (unabhängig vom Ausnahmetyp, den Sie abfangen). Verwenden Sie zum erneuten Werfen throw;. Bei ersteren sieht die Ausnahme so aus, als stamme sie von Ihrer, throw exwährend sie bei letzteren ordnungsgemäß von der ursprünglichen throwAussage stammt.
Ein Lebenslauf vom

2
Warum haken Sie das Application.ThreadExceptionEreignis ein und schließen jede Ausnahme mit einem ein catch(Exception ex) {ex.Log(ex);}. Ich würde wahrscheinlich zustimmen, dass Ersteres eine ausgezeichnete Praxis ist, aber Letzteres erhöht das Risiko, dass Ihre Fehlerprotokolle dupliziert werden, und verbirgt, dass die Ausnahme aufgetreten ist. Ist auch throw exsehr sehr schlecht.
Keith

1
Ich habe verstanden über catch (Exception ex) {throw ex; } nutzlos sein. Ich nehme an, "redundant" ist nicht das beste Wort für "Tu das nicht". Deshalb habe ich den Beitrag ein wenig geändert, um besser zu sagen, dass die beiden ersten Beispiele für Try Catch vermieden werden müssen.
Larry

3
Tolle und konstruktive Antwort, am meisten hat mir der Satz Nur Luft gefallen :) Und danke für die Application.ThreadExceptionVeranstaltung, das war mir nicht bewusst, sehr nützlich.
Mahdi Tahsildari


61

Die beste Vorgehensweise ist, dass die Ausnahmebehandlung niemals Probleme verbergen sollte . Dies bedeutet, dass try-catchBlöcke äußerst selten sein sollten.

Es gibt 3 Umstände, unter denen die Verwendung von a try-catchsinnvoll ist.

  1. Gehen Sie immer so tief wie möglich mit bekannten Ausnahmen um. Wenn Sie jedoch eine Ausnahme erwarten, ist es normalerweise besser, diese zuerst zu testen. Zum Beispiel werden Analyse-, Formatierungs- und arithmetische Ausnahmen fast immer besser zuerst durch Logikprüfungen behandelt als durch bestimmte try-catch.

  2. Wenn Sie für eine Ausnahme etwas tun müssen (z. B. Protokollierung oder Rollback einer Transaktion), lösen Sie die Ausnahme erneut aus.

  3. Behandeln Sie unbekannte Ausnahmen immer so hoch wie möglich - der einzige Code, der eine Ausnahme verbrauchen und nicht erneut auslösen sollte, sollte die Benutzeroberfläche oder die öffentliche API sein.

Angenommen, Sie stellen eine Verbindung zu einer Remote-API her. Hier müssen Sie bestimmte Fehler erwarten (und haben unter diesen Umständen Probleme). Dies ist also Fall 1:

try 
{
    remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex) 
{
    // User's security details have expired
    return false;
}

return true;

Beachten Sie, dass keine anderen Ausnahmen abgefangen werden, da sie nicht erwartet werden.

Angenommen, Sie versuchen, etwas in der Datenbank zu speichern. Wir müssen es zurücksetzen, wenn es fehlschlägt, also haben wir Fall 2:

try
{
    DBConnection.Save();
}
catch
{
    // Roll back the DB changes so they aren't corrupted on ANY exception
    DBConnection.Rollback();

    // Re-throw the exception, it's critical that the user knows that it failed to save
    throw;
}

Beachten Sie, dass wir die Ausnahme erneut auslösen - der Code weiter oben muss noch wissen, dass etwas fehlgeschlagen ist.

Endlich haben wir die Benutzeroberfläche - hier wollen wir keine völlig unbehandelten Ausnahmen haben, aber wir wollen sie auch nicht verbergen. Hier haben wir ein Beispiel für Fall 3:

try
{
    // Do something
}
catch(Exception ex) 
{
    // Log exception for developers
    WriteException2LogFile(ex);

    // Display message to users
    DisplayWarningBox("An error has occurred, please contact support!");
}

Die meisten API- oder UI-Frameworks bieten jedoch generische Methoden für Fall 3. Beispielsweise verfügt ASP.Net über einen gelben Fehlerbildschirm, in dem die Ausnahmedetails ausgegeben werden, der jedoch in der Produktionsumgebung durch eine allgemeinere Meldung ersetzt werden kann. Das Befolgen dieser Anweisungen ist eine bewährte Methode, da Sie viel Code sparen, aber auch, weil die Fehlerprotokollierung und -anzeige eher Konfigurationsentscheidungen als fest codiert sein sollten.

Dies alles bedeutet, dass Fall 1 (bekannte Ausnahmen) und Fall 3 (einmalige Behandlung der Benutzeroberfläche) bessere Muster aufweisen (vermeiden Sie den erwarteten Fehler oder die Handfehlerbehandlung an die Benutzeroberfläche).

Selbst Fall 2 kann durch bessere Muster ersetzt werden. Beispielsweise erschweren Transaktionsbereiche ( usingBlöcke, die Transaktionen zurücksetzen , die während des Blocks nicht festgeschrieben wurden) Entwicklern, das Best-Practice-Muster falsch zu verstehen.

Angenommen, Sie haben eine umfangreiche ASP.Net-Anwendung. Die Fehlerprotokollierung kann über ELMAH erfolgen , die Fehleranzeige kann ein informatives YSoD lokal und eine nette lokalisierte Nachricht in der Produktion sein. Datenbankverbindungen können alle über Transaktionsbereiche und using-blöcke erfolgen. Sie brauchen keinen einzigen try-catchBlock.

TL; DR: Best Practice ist es, überhaupt keine try-catchBlöcke zu verwenden.


4
@Jorj du solltest den ganzen Beitrag lesen, und wenn du immer noch nicht einverstanden bist, wäre es vielleicht konstruktiver, einem meiner unterstützenden Argumente entgegenzutreten, als nur zu sagen, dass dir meine Schlussfolgerung nicht gefällt. Es gibt fast immer ein besseres Muster als try-catch- es kann (sehr gelegentlich) nützlich sein und ich argumentiere nicht, dass Sie sie niemals verwenden sollten, aber in 99% der Fälle gibt es einen besseren Weg.
Keith

Die mit Abstand beste Antwort: Fast jeder .net-Entwicklungstyp verfügt über einen HANDLER, der sich viel besser für den Umgang mit Ausnahmen auf globaler Ebene eignet. Dies erleichtert die konsistente Handhabung von Ausnahmen und das einfache Ausblasen In der Entwicklung (warum sollte jemand eine Protokolldatei nach einem Stack-Trace durchsuchen wollen?) @Kieth, ich würde mit Ihrem TLDR führen und einige Beispiele für die globalen Handler hinzufügen (z. B. ThreadException, Application_Error usw.). Auf jeden Fall SPEZIFISCHE Fehler abfangen, aber es ist verrückt, jemals eine Methode in einen try / catch / log zu
packen

34

Eine Ausnahme ist ein Blockierungsfehler .

Zunächst sollte die beste Vorgehensweise darin bestehen, keine Ausnahmen für Fehler jeglicher Art auszulösen, es sei denn, es handelt sich um einen Blockierungsfehler .

Wenn der Fehler blockiert , lösen Sie die Ausnahme aus. Sobald die Ausnahme bereits ausgelöst wurde, müssen Sie sie nicht mehr ausblenden, da sie außergewöhnlich ist. Informieren Sie den Benutzer darüber (Sie sollten die gesamte Ausnahme in etwas umformatieren, das für den Benutzer in der Benutzeroberfläche nützlich ist).

Ihre Aufgabe als Softwareentwickler ist es, einen Ausnahmefall zu vermeiden , in dem bestimmte Parameter oder Laufzeitsituationen in einer Ausnahme enden können. Das heißt, Ausnahmen dürfen nicht stummgeschaltet werden, aber diese müssen vermieden werden .

Wenn Sie beispielsweise wissen, dass eine Ganzzahl- Eingabe ein ungültiges Format haben kann, verwenden Sie int.TryParsestattdessen anstelle von int.Parse. Es gibt viele Fälle, in denen Sie dies tun können, anstatt nur zu sagen: "Wenn dies fehlschlägt, lösen Sie einfach eine Ausnahme aus."

Ausnahmen zu werfen ist teuer.

Wenn schließlich eine Ausnahme ausgelöst wird, anstatt die Ausnahme nach dem Auslösen in das Protokoll zu schreiben, besteht eine der besten Methoden darin, sie in einem Ausnahmebehandler der ersten Chance abzufangen . Beispielsweise:

  • ASP.NET: Global.asax Application_Error
  • Andere: AppDomain.FirstChanceException-Ereignis .

Mein Standpunkt ist, dass lokale Versuche / Fänge besser für die Behandlung von Sonderfällen geeignet sind, in denen Sie eine Ausnahme in eine andere übersetzen können oder wenn Sie sie für einen sehr, sehr, sehr, sehr, sehr speziellen Fall (einen Bibliotheksfehler) "stumm schalten" möchten eine nicht verwandte Ausnahme auslösen, die Sie stummschalten müssen, um den gesamten Fehler zu umgehen).

Für den Rest der Fälle:

  • Vermeiden Sie Ausnahmen.
  • Wenn dies nicht möglich ist: Ausnahmebehandlungsroutinen der ersten Chance.
  • Oder verwenden Sie einen PostSharp-Aspekt (AOP).

Antwort auf @thewhiteambit auf einen Kommentar ...

@thewhiteambit sagte:

Ausnahmen sind keine schwerwiegenden Fehler, sondern Ausnahmen! Manchmal sind sie nicht einmal Fehler, aber sie als schwerwiegende Fehler zu betrachten, ist ein völlig falsches Verständnis dessen, was Ausnahmen sind.

Wie kann eine Ausnahme nicht einmal ein Fehler sein?

  • Keine Datenbankverbindung => Ausnahme.
  • Ungültiges Zeichenfolgenformat zum Parsen auf eine Ausnahme vom Typ =>
  • Der Versuch, JSON zu analysieren, und während der Eingabe ist eigentlich keine JSON => Ausnahme
  • Argument, nullwährend Objekt erwartet wurde => Ausnahme
  • Einige Bibliotheken haben einen Fehler => löst eine unerwartete Ausnahme aus
  • Es gibt eine Socket-Verbindung und die Verbindung wird getrennt. Dann versuchen Sie eine Nachricht zu senden => Ausnahme
  • ...

Wir könnten 1k Fälle auflisten, in denen eine Ausnahme ausgelöst wird, und schließlich wird jeder der möglichen Fälle ein Fehler sein .

Eine Ausnahme ist ein Fehler, da es sich letztendlich um ein Objekt handelt, das Diagnoseinformationen sammelt - es hat eine Meldung und es passiert, wenn etwas schief geht.

Niemand würde eine Ausnahme auslösen, wenn es keinen Ausnahmefall gibt. Ausnahmen sollten blockierende Fehler sein, da Ihre Anwendung / Ihr Dienst den Vorgang, der in einen Ausnahmefall eingetreten ist, stoppt , wenn Sie nicht versuchen, in die Verwendung try / catch und Ausnahmen zum Implementieren des Kontrollflusses zu fallen .

Außerdem empfehle ich jedem, das von Martin Fowler veröffentlichte (und von Jim Shore geschriebene) Fail-Fast- Paradigma zu überprüfen . So habe ich immer verstanden, wie man mit Ausnahmen umgeht, noch bevor ich vor einiger Zeit zu diesem Dokument kam.

[...] betrachten sie als schwerwiegende Fehler ist ein völlig falsches Verständnis der Ausnahmen.

Normalerweise unterbrechen Ausnahmen den Betriebsablauf und werden so behandelt, dass sie in vom Menschen verständliche Fehler umgewandelt werden. Daher scheint eine Ausnahme tatsächlich ein besseres Paradigma zu sein, um Fehlerfälle zu behandeln und zu bearbeiten, um einen vollständigen Absturz von Anwendung / Dienst zu vermeiden und den Benutzer / Verbraucher darüber zu informieren, dass ein Fehler aufgetreten ist.

Weitere Antworten zu @ thewhiteambit-Bedenken

Zum Beispiel könnte das Programm im Falle einer fehlenden Datenbankverbindung ausnahmsweise mit dem Schreiben in eine lokale Datei fortfahren und die Änderungen an die Datenbank senden, sobald sie wieder verfügbar ist. Es könnte versucht werden, Ihr ungültiges String-To-Number-Casting erneut mit sprachlokaler Interpretation in Ausnahme zu analysieren, z. B. wenn Sie versuchen, die englische Standardsprache zu analysieren ("1,5"), schlägt dies fehl und Sie versuchen es erneut mit deutscher Interpretation, was vollständig ist gut, weil wir Komma anstelle von Punkt als Trennzeichen verwenden. Sie sehen, dass diese Ausnahmen nicht einmal blockiert werden dürfen, sondern nur eine Ausnahmebehandlung erfordern.

  1. Wenn Ihre App möglicherweise offline funktioniert, ohne dass Daten in der Datenbank gespeichert werden, sollten Sie keine Ausnahmen verwenden , da die Implementierung des Kontrollflusses mithilfe von try/catchals Anti-Pattern betrachtet wird. Offline-Arbeit ist ein möglicher Anwendungsfall. Sie implementieren also den Kontrollfluss, um zu überprüfen, ob auf die Datenbank zugegriffen werden kann oder nicht. Sie warten nicht, bis sie nicht mehr erreichbar ist .

  2. Das Parsen ist auch ein erwarteter Fall ( kein AUSSERGEWÖHNLICHER FALL ). Wenn Sie dies erwarten, verwenden Sie keine Ausnahmen, um den Fluss zu steuern! . Sie erhalten vom Benutzer einige Metadaten, um zu wissen, was seine / ihre Kultur ist, und Sie verwenden dafür Formatierer! .NET unterstützt auch diese und andere Umgebungen und eine Ausnahme, da die Formatierung von Zahlen vermieden werden muss, wenn Sie eine kulturspezifische Nutzung Ihrer Anwendung / Ihres Dienstes erwarten .

Eine nicht behandelte Ausnahme wird normalerweise zu einem Fehler, aber Ausnahmen selbst sind nicht codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors

Dieser Artikel ist nur eine Meinung oder ein Standpunkt des Autors.

Da Wikipedia auch nur die Meinung von Autoren sein kann, würde ich nicht sagen, dass es das Dogma ist , aber überprüfen Sie, was der Artikel Coding by Exception irgendwo in einem Absatz sagt:

[...] Die Verwendung dieser Ausnahmen zur Behandlung bestimmter Fehler, die beim Fortfahren des Programms auftreten, wird als Codierung nach Ausnahme bezeichnet. Dieses Anti-Pattern kann die Leistung und Wartbarkeit von Software schnell beeinträchtigen.

Es heißt auch irgendwo:

Falsche Verwendung von Ausnahmen

Oft kann das Codieren nach Ausnahmen zu weiteren Problemen in der Software mit falscher Ausnahmeverwendung führen. Zusätzlich zur Verwendung der Ausnahmebehandlung für ein eindeutiges Problem führt die falsche Verwendung von Ausnahmen dazu, dass Code auch nach dem Auslösen der Ausnahme ausgeführt wird. Diese schlechte Programmiermethode ähnelt der goto-Methode in vielen Softwaresprachen, tritt jedoch erst auf, nachdem ein Problem in der Software erkannt wurde.

Ehrlich gesagt glaube ich, dass Software nicht entwickelt werden kann, wenn Anwendungsfälle nicht ernst genommen werden. Wenn Sie das wissen ...

  • Ihre Datenbank kann offline gehen ...
  • Einige Dateien können gesperrt werden ...
  • Einige Formatierungen werden möglicherweise nicht unterstützt ...
  • Einige Domain-Überprüfungen schlagen möglicherweise fehl ...
  • Ihre App sollte im Offline-Modus funktionieren ...
  • welcher Anwendungsfall auch immer ...

... Sie werden dafür keine Ausnahmen verwenden . Sie würden unterstützen diese Anwendungsfälle mit regelmäßigen Kontrollfluss.

Und wenn ein unerwarteter Anwendungsfall nicht behandelt wird, schlägt Ihr Code schnell fehl, da eine Ausnahme ausgelöst wird . Richtig, denn eine Ausnahme ist ein Ausnahmefall .

Auf der anderen Seite und schließlich decken Sie manchmal Ausnahmefälle ab , die erwartete Ausnahmen auslösen , aber Sie werfen sie nicht aus, um den Kontrollfluss zu implementieren. Sie tun dies, weil Sie den oberen Ebenen mitteilen möchten, dass Sie einen Anwendungsfall nicht unterstützen oder Ihr Code mit bestimmten Argumenten oder Umgebungsdaten / -eigenschaften nicht funktioniert.


6

Das einzige Mal, wenn Sie Ihre Benutzer über etwas beunruhigen sollten, das im Code passiert ist, ist, wenn sie etwas tun können oder müssen, um das Problem zu vermeiden. Wenn sie Daten in einem Formular ändern können, drücken Sie eine Taste oder ändern Sie eine Anwendungseinstellung, um das Problem zu vermeiden, und lassen Sie sie dies wissen. Warnungen oder Fehler, die der Benutzer nicht vermeiden kann, führen jedoch dazu, dass er das Vertrauen in Ihr Produkt verliert.

Ausnahmen und Protokolle sind für Sie, den Entwickler, und nicht für Ihren Endbenutzer. Es ist weitaus besser zu verstehen, was zu tun ist, wenn Sie jede Ausnahme abfangen, als nur eine goldene Regel anzuwenden oder sich auf ein anwendungsweites Sicherheitsnetz zu verlassen.

Gedankenlose Codierung ist die EINZIGE Art der falschen Codierung. Die Tatsache, dass Sie der Meinung sind, dass in diesen Situationen etwas Besseres getan werden kann, zeigt, dass Sie in eine gute Codierung investiert sind. Vermeiden Sie es jedoch, in diesen Situationen eine generische Regel zu stempeln, und verstehen Sie den Grund, warum etwas überhaupt geworfen werden muss, und was Sie können tun, um sich davon zu erholen.


6

Ich weiß, dass dies eine alte Frage ist, aber hier hat niemand den MSDN-Artikel erwähnt, und es war das Dokument, das ihn tatsächlich für mich geklärt hat. MSDN hat ein sehr gutes Dokument dazu. Sie sollten Ausnahmen abfangen, wenn die folgenden Bedingungen erfüllt sind:

  • Sie haben ein gutes Verständnis dafür, warum die Ausnahme möglicherweise ausgelöst wird, und können eine bestimmte Wiederherstellung implementieren, z. B. den Benutzer auffordern, einen neuen Dateinamen einzugeben, wenn Sie ein FileNotFoundException-Objekt abfangen.

  • Sie können eine neue, spezifischere Ausnahme erstellen und auslösen.

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}
  • Sie möchten eine Ausnahme teilweise behandeln, bevor Sie sie zur weiteren Behandlung weitergeben. Im folgenden Beispiel wird ein catch-Block verwendet, um einem Fehlerprotokoll einen Eintrag hinzuzufügen, bevor die Ausnahme erneut ausgelöst wird.
    try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}

Ich würde empfehlen, den gesamten Abschnitt " Ausnahmen und Ausnahmebehandlung " sowie die Best Practices für Ausnahmen zu lesen .


1

Der bessere Ansatz ist der zweite (der, in dem Sie den Ausnahmetyp angeben). Dies hat den Vorteil, dass Sie wissen, dass diese Art von Ausnahme in Ihrem Code auftreten kann. Sie behandeln diese Art von Ausnahme und können fortfahren. Wenn eine andere Ausnahme aufgetreten ist, bedeutet dies, dass etwas nicht stimmt, was Ihnen hilft, Fehler in Ihrem Code zu finden. Die Anwendung wird irgendwann abstürzen, aber Sie werden feststellen, dass Sie etwas verpasst haben (Fehler), das behoben werden muss.


1

Mit Ausnahmen versuche ich Folgendes:

Zuerst fange ich spezielle Arten von Ausnahmen wie Division durch Null, E / A-Operationen usw. ab und schreibe entsprechend Code. Zum Beispiel eine Division durch Null, abhängig von der Herkunft der Werte, die ich den Benutzer alarmieren könnte (zum Beispiel ein einfacher Taschenrechner, der in einer mittleren Berechnung (nicht die Argumente) in einer Division durch Null ankommt) oder um diese Ausnahme stillschweigend zu behandeln, Protokollierung es und weiter zu verarbeiten.

Dann versuche ich, die verbleibenden Ausnahmen abzufangen und zu protokollieren. Wenn möglich, erlauben Sie die Ausführung von Code, andernfalls benachrichtigen Sie den Benutzer, dass ein Fehler aufgetreten ist, und bitten Sie ihn, einen Fehlerbericht zu senden.

Im Code so etwas:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}

1
Teilen durch Null Ausnahmen und dergleichen werden besser behandelt, indem 0vorher nach einem Zähler gesucht wird, anstatt nach a try-catch. Auch warum das Generikum Exceptionhier fangen ? Es ist besser, den Fehler in die Luft sprudeln zu lassen, als ihn hier in allen Fällen zu behandeln, in denen Sie ihn nicht erwarten.
Keith

Lesen Sie besser, was ich über das Beispiel geschrieben habe - beachten Sie das "nicht in Argumenten". Natürlich sollte jeder Rechner die angegebenen Argumente überprüfen. Was ich sprach, war über die mittleren Schritte. Zu diesem Zeitpunkt wurde die Überprüfung der Benutzerargumente bereits durchgeführt. In einigen Anwendungen ist es auch besser, Ausnahmen beim Sprudeln zu vermeiden. Einige Apps sollten Ausnahmen still behandeln, während andere Ausnahmen als Fehler behandeln sollten. Ein Webserver sollte beispielsweise auch dann ausgeführt werden, wenn Ausnahmen auftreten, wobei medizinische Software (z. B. Röntgengeräte) abgebrochen werden sollte, wenn Ausnahmen auftreten.
Sorcerer86pt

Keine App sollte Ausnahmen jemals still behandeln. Gelegentlich haben Sie eine Ausnahme, die der Code verarbeiten kann, aber eine solche Verwendung sollte sowohl selten als auch spezifisch für die erwartete Ausnahme sein. Ihr Beispiel für einen Webserver ist schlecht - er sollte über Konfigurationseinstellungen verfügen, mit denen Sie auswählen können, wie Fehler protokolliert werden und ob sie detailliert oder nur auf einer HTTP 500-Seite angezeigt werden. Fehler sollten jedoch niemals stillschweigend ignoriert werden.
Keith

Ich versuche herauszufinden, was die Leute wirklich motiviert, noch ein Synonym für "goto" hinzuzufügen. Aber in Bezug auf die Division durch Null wäre dies die EINE Art von Ausnahme, die die Sprachverbesserung rechtfertigt. Warum? Weil es durchaus der Fall sein kann, dass A) Null statistisch ein Infinitesimalwert in Ihrem Datensatz ist und B) das Verwenden (Zulassen) einer Ausnahme viel effizienter sein kann, da die Division eine Möglichkeit ist, auf einen Nullteiler zu testen. Wenn A und B wahr sind, ist die durchschnittliche Ausführung Ihres Programms mit einer Ausnahme schneller und möglicherweise sogar kleiner.
Mike Layton

1

Der zweite Ansatz ist gut.

Wenn Sie den Fehler nicht anzeigen und den Benutzer der Anwendung verwirren möchten, indem Sie eine Laufzeitausnahme (dh einen Fehler) anzeigen, die nicht mit ihnen zusammenhängt, protokollieren Sie einfach den Fehler, und das technische Team kann nach dem Problem suchen und es beheben.

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);//it will write the or log the error in a text file
}

Ich empfehle Ihnen, den zweiten Ansatz für Ihre gesamte Anwendung zu wählen.


2
Der zweite Ansatz zeigt dem Benutzer nicht an, dass ein Fehler aufgetreten ist. Wenn er beispielsweise etwas gespeichert hat, weiß er nicht, dass es fehlgeschlagen ist. catchBlöcke sollten immer entweder aufrufen, throwum die Ausnahme zu aktivieren oder etwas zurückzugeben / etwas anzuzeigen, das dem Benutzer mitteilt, dass die Aktion fehlgeschlagen ist. Sie möchten den Support-Anruf erhalten, wenn sie nicht speichern, was auch immer es ist, und nicht 6 Monate später, wenn sie versuchen, es abzurufen und es nicht finden können.
Keith

0

Leeren Fangblock lassen ist das Schlimmste. Wenn ein Fehler auftritt, können Sie ihn am besten beheben:

  1. Melden Sie es in Datei \ Datenbank etc ..
  2. Versuchen Sie, das Problem im laufenden Betrieb zu beheben (versuchen Sie möglicherweise eine alternative Methode, um diesen Vorgang auszuführen).
  3. Wenn wir das nicht beheben können, benachrichtigen Sie den Benutzer über einen Fehler und brechen Sie den Vorgang natürlich ab

0

Die Behandlung von Ausnahmen kann für mich als Geschäftsregel angesehen werden. Offensichtlich ist der erste Ansatz nicht akzeptabel. Der zweite ist besser und könnte zu 100% korrekt sein, wenn der Kontext dies sagt. Jetzt entwickeln Sie beispielsweise ein Outlook-Add-In. Wenn Sie beim Add-In eine nicht behandelte Ausnahme auslösen, weiß dies möglicherweise der Outlook-Benutzer, da sich der Outlook aufgrund eines fehlgeschlagenen Plugins nicht selbst zerstört. Und es fällt Ihnen schwer herauszufinden, was schief gelaufen ist. Daher ist der zweite Ansatz in diesem Fall für mich der richtige. Neben der Protokollierung der Ausnahme können Sie sich auch dafür entscheiden, dem Benutzer eine Fehlermeldung anzuzeigen - ich betrachte dies als Geschäftsregel.


0

Es wird empfohlen, eine Ausnahme auszulösen, wenn der Fehler auftritt. Weil ein Fehler aufgetreten ist und dieser nicht ausgeblendet werden sollte.

Aber im wirklichen Leben kann es mehrere Situationen geben, in denen Sie dies verbergen möchten

  1. Sie verlassen sich auf Komponenten von Drittanbietern und möchten das Programm im Fehlerfall fortsetzen.
  2. Sie haben einen Business Case, den Sie im Fehlerfall fortsetzen müssen

6
Nein . Sie nicht werfen Exception. Je. Werfen Sie eine geeignete Unterklasse von Exceptionallem, was Sie wollen, aber niemals, Exceptionweil dies absolut keine semantischen Informationen liefert. Ich kann kein Szenario sehen, in dem es sinnvoll ist, zu werfen, Exceptionaber keine Unterklasse davon.
Ein Lebenslauf


0

Das catchohne Argumente frisst einfach die Ausnahme und nützt nichts. Was ist, wenn ein schwerwiegender Fehler auftritt? Es gibt keine Möglichkeit zu wissen, was passiert ist, wenn Sie catch ohne Argument verwenden.

Eine catch-Anweisung sollte spezifischere Ausnahmen wie abfangen, FileNotFoundExceptionund ganz am Ende sollten Sie abfangen, Exceptionwelche andere Ausnahmen abfangen und protokollieren würden.


Warum catch(Exception)am Ende einen General ? Wenn Sie es nicht erwarten, ist es immer empfehlenswert, es an die nächste Ebene weiterzugeben.
Keith

1
@ Keith ja, Sie haben Recht ... es hat keinen Sinn, Ausnahmen zu fangen, die Sie nicht erwarten, aber Sie können die allgemeine Ausnahme für Protokollierungszwecke ..
Anirudha

0

Manchmal müssen Sie Ausnahmen behandeln, die den Benutzern nichts sagen.

Mein Weg ist:

  • Nicht erfasste Ausnahmen auf Anwendungsebene (dh in global.asax) für kritische Ausnahmen abfangen (Anwendung kann nicht nützlich sein). Diese Ausnahmen fange ich nicht an. Melden Sie sie einfach auf App-Ebene an und lassen Sie das System seine Arbeit erledigen.
  • Fangen Sie "on place" und zeigen Sie dem Benutzer einige nützliche Informationen (falsche Nummer eingegeben, kann nicht analysiert werden).
  • Fangen Sie an Ort und Stelle und tun Sie nichts bei geringfügigen Problemen wie "Ich werde im Hintergrund nach Aktualisierungsinformationen suchen, aber der Dienst wird nicht ausgeführt".

Es muss definitiv keine Best Practice sein. ;-);


0

Ich kann dir etwas sagen:

Snippet Nr. 1 ist nicht akzeptabel, da Ausnahmen ignoriert werden. (Es schluckt es, als wäre nichts passiert).

Fügen Sie also keinen Fangblock hinzu, der nichts tut oder nur neu wirft.

Der Catch-Block sollte einen Mehrwert bieten. Zum Beispiel Nachricht an Endbenutzer ausgeben oder Fehler protokollieren.

Verwenden Sie keine Ausnahme für die normale Ablaufprogrammlogik. Beispielsweise:

zB Eingabevalidierung. <- Dies ist keine gültige Ausnahmesituation. Sie sollten vielmehr eine Methode schreiben, um IsValid(myInput);zu überprüfen, ob das Eingabeelement gültig ist oder nicht.

Designcode zur Vermeidung von Ausnahmen. Beispielsweise:

int Parse(string input);

Wenn wir einen Wert übergeben, der nicht an int analysiert werden kann, würde diese Methode eine Ausnahme auslösen, stattdessen könnten wir Folgendes schreiben:

bool TryParse(string input,out int result); <- Diese Methode würde einen Booleschen Wert zurückgeben, der angibt, ob die Analyse erfolgreich war.

Vielleicht liegt dies etwas außerhalb des Rahmens dieser Frage, aber ich hoffe, dies wird Ihnen helfen, die richtigen Entscheidungen zu treffen, wenn es um try {} catch(){}Ausnahmen geht.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.