Best Practice für die Ausnahmebehandlung in einer Windows Forms-Anwendung?

118

Ich bin gerade dabei, meine erste Windows Forms-Anwendung zu schreiben. Ich habe jetzt ein paar C # -Bücher gelesen, damit ich relativ gut verstehe, welche Sprachfunktionen C # mit Ausnahmen zu tun hat. Sie sind jedoch alle ziemlich theoretisch. Was ich noch nicht habe, ist ein Gefühl dafür, wie ich die Grundkonzepte in ein gutes Modell für die Ausnahmebehandlung in meiner Anwendung übersetzen kann.

Möchte jemand Perlen der Weisheit zu diesem Thema teilen? Veröffentlichen Sie alle häufigen Fehler, die Neulinge wie ich gemacht haben, und allgemeine Ratschläge zum Umgang mit Ausnahmen auf eine Weise, die meine Anwendung stabiler und robuster macht.

Die wichtigsten Dinge, die ich gerade zu trainieren versuche, sind:

  • Wann sollte ich eine Ausnahme erneut auslösen?
  • Sollte ich versuchen, einen zentralen Fehlerbehandlungsmechanismus zu haben?
  • Haben die Behandlung von Ausnahmen, die möglicherweise ausgelöst werden, einen Leistungseinbruch im Vergleich zu vorbeugenden Tests, z. B. ob eine Datei auf der Festplatte vorhanden ist?
  • Sollte der gesamte ausführbare Code in Try-Catch-finally-Blöcken eingeschlossen sein?
  • Gibt es Zeiten, in denen ein leerer Fangblock akzeptabel sein könnte?

Alle Ratschläge dankbar erhalten!

Jon Artus
quelle

Antworten:

79

Noch ein paar Kleinigkeiten ...

Sie sollten unbedingt über eine zentralisierte Richtlinie zur Ausnahmebehandlung verfügen. Dies kann so einfach sein wie das Einschließen Main()eines Try / Catch-Vorgangs, der schnell mit einer ordnungsgemäßen Fehlermeldung an den Benutzer fehlschlägt. Dies ist der Ausnahmebehandler "letzter Ausweg".

Präventivprüfungen sind immer korrekt, wenn möglich, aber nicht immer perfekt. Beispielsweise könnte zwischen dem Code, in dem Sie die Existenz einer Datei überprüfen, und der nächsten Zeile, in der Sie sie öffnen, die Datei gelöscht worden sein oder ein anderes Problem könnte Ihren Zugriff behindern. Du musst noch versuchen / fangen / endlich in dieser Welt. Verwenden Sie je nach Bedarf sowohl die präventive Prüfung als auch try / catch / finally.

Schlucken Sie niemals eine Ausnahme, außer in den am besten dokumentierten Fällen, in denen Sie absolut sicher sind, dass die ausgelöste Ausnahme lebenswert ist. Dies wird fast nie der Fall sein. (Und wenn ja , stellen Sie sicher, dass Sie nur die bestimmte Ausnahmeklasse schlucken - schlucken Sie niemalsSystem.Exception .)

Schlucken Sie beim Erstellen von Bibliotheken (die von Ihrer App verwendet werden) keine Ausnahmen und haben Sie keine Angst, die Ausnahmen in die Luft sprudeln zu lassen. Werfen Sie nicht erneut, es sei denn, Sie haben etwas Nützliches hinzuzufügen. Tun Sie dies niemals (in C #):

throw ex;

Da werden Sie den Aufrufstapel löschen. Wenn Sie erneut werfen müssen (was gelegentlich erforderlich ist, z. B. bei Verwendung des Ausnahmebehandlungsblocks der Unternehmensbibliothek), verwenden Sie Folgendes:

throw;

Letztendlich sollte die überwiegende Mehrheit der von einer laufenden Anwendung ausgelösten Ausnahmen irgendwo offengelegt werden. Sie sollten nicht Endbenutzern ausgesetzt werden (da sie häufig proprietäre oder anderweitig wertvolle Daten enthalten), sondern in der Regel protokolliert werden, wobei Administratoren über die Ausnahme informiert werden. Dem Benutzer kann ein allgemeines Dialogfeld angezeigt werden, möglicherweise mit einer Referenznummer, um die Dinge einfach zu halten.

Ausnahmebehandlung in .NET ist mehr Kunst als Wissenschaft. Jeder wird seine Favoriten hier teilen können. Dies sind nur einige der Tipps, die ich seit Tag 1 mit .NET erhalten habe. Diese Techniken haben meinen Speck mehr als einmal gerettet. Ihr Kilometerstand kann variieren.

John Rudy
quelle
1
Wenn Ausnahmen auftauchen, wie soll der Anrufer wissen, ob die Ausnahme anzeigt, dass ein Vorgang fehlgeschlagen ist, das System jedoch grundsätzlich in Ordnung ist (z. B. hat ein Benutzer versucht, eine beschädigte Dokumentdatei zu öffnen; die Datei wird nicht geladen, sondern alles sonst sollte es in Ordnung sein), oder ob es anzeigt, dass die CPU in Flammen steht und man so schnell wie möglich zu den Ausgängen gehen sollte? So etwas wie ArgumentException könnte entweder anzeigen, abhängig von den Umständen, unter denen es ausgelöst wird.
Supercat
2
@supercat Durch Schreiben spezifischer Unterklassen ApplicationExceptionfür die Fehlerfälle, die die Anwendung vernünftigerweise unterscheiden und behandeln sollte.
Matt Enright
@Matt Enright: Natürlich ist es möglich, eigene Ausnahmen abzufangen, aber mir ist nichts bekannt, das einer Konvention ähnelt, nach der Module, die Ausnahmen auslösen, angeben, ob sie die Beschädigung eines Zustands außerhalb der durch den Fehler implizierten Zustände anzeigen. Im Idealfall würde eine Methode wie SuperDocument.CreateFromFile () entweder erfolgreich sein, eine CleanFailure-Ausnahme auslösen oder eine SomethingReallyBadHappenedException auslösen. Leider kann man nicht wissen, ob eine InvalidOperationException ...
Supercat
@Matt Enright: ... sollte in eine CleanFailureException oder eine SomethingReallyBadHappenedException eingeschlossen werden. Und alles in einzelne Try-Catch-Blöcke zu packen, würde den ganzen Zweck, Ausnahmen zu haben, zunichte machen.
Supercat
2
Ich denke, das ist ein schlechtes Bibliotheksdesign, um Fehlermodi mit einem ungenauen Ausnahmetyp zu verbergen. Wirf keine FileNotFoundException, wenn du eigentlich eine IOException oder eine InvalidDataException meinst, da die Anwendung auf jeden Fall anders reagieren muss. Dinge wie StackOverflowException oder OutOfMemoryException können von einer Anwendung nicht vernünftigerweise verarbeitet werden. Lassen Sie sie also einfach sprudeln, da in einer gut erzogenen Anwendung der zentralisierte "Last-Resort" -Handler sowieso vorhanden ist.
Matt Enright
63

Hier gibt es einen ausgezeichneten CodeProject-Artikel . Hier einige Highlights:

  • Planen Sie das Schlimmste *
  • Überprüfen Sie es früh
  • Vertraue keinen externen Daten
  • Die einzigen zuverlässigen Geräte sind: Video, Maus und Tastatur.
  • Schreibvorgänge können ebenfalls fehlschlagen
  • Code sicher
  • Wirf keine neue Ausnahme ()
  • Fügen Sie keine wichtigen Ausnahmeinformationen in das Feld Nachricht ein
  • Setzen Sie einen einzelnen Haken (Ausnahme ex) pro Thread
  • Generische Ausnahmen sollten veröffentlicht werden
  • Log Exception.ToString (); Niemals nur Exception.Message protokollieren!
  • Fang (Ausnahme) nicht mehr als einmal pro Thread
  • Schlucken Sie niemals Ausnahmen
  • Der Bereinigungscode sollte in endgültige Blöcke eingefügt werden
  • Verwenden Sie "using" überall
  • Geben Sie unter Fehlerbedingungen keine speziellen Werte zurück
  • Verwenden Sie keine Ausnahmen, um das Fehlen einer Ressource anzuzeigen
  • Verwenden Sie die Ausnahmebehandlung nicht als Mittel zum Zurückgeben von Informationen von einer Methode
  • Verwenden Sie Ausnahmen für Fehler, die nicht ignoriert werden sollten
  • Löschen Sie die Stapelverfolgung nicht, wenn Sie eine Ausnahme erneut auslösen
  • Vermeiden Sie es, Ausnahmen zu ändern, ohne einen semantischen Wert hinzuzufügen
  • Ausnahmen sollten mit [Serializable] gekennzeichnet werden.
  • Wenn Sie Zweifel haben, behaupten Sie nicht, werfen Sie eine Ausnahme
  • Jede Ausnahmeklasse sollte mindestens die drei ursprünglichen Konstruktoren haben
  • Seien Sie vorsichtig, wenn Sie das Ereignis AppDomain.UnhandledException verwenden
  • Das Rad nicht neu erfinden
  • Verwenden Sie nicht die unstrukturierte Fehlerbehandlung (VB.Net).
Micah
quelle
3
Würde es Ihnen etwas ausmachen, mir diesen Punkt etwas näher zu erläutern? "Verwenden Sie keine Ausnahmen, um das Fehlen einer Ressource anzuzeigen." Ich bin mir nicht ganz sicher, ob ich den Grund dafür verstehe. Auch nur eine Bemerkung: Diese Antwort erklärt das "Warum" überhaupt nicht. Ich weiß, dass dies 5 Jahre alt ist, aber es nervt mich immer noch ein bisschen
Rémi
Der Link zum Artikel, auf den verwiesen wird, ist abgelaufen. Code Project schlägt vor, dass der Artikel möglicherweise hierher verschoben wurde .
DavidRR
15

Beachten Sie, dass Windows Forms über einen eigenen Mechanismus zur Behandlung von Ausnahmen verfügt. Wenn auf eine Schaltfläche im Formular geklickt wird und der Handler eine Ausnahme auslöst, die nicht im Handler abgefangen ist, zeigt Windows Forms ein eigenes Dialogfeld für nicht behandelte Ausnahmen an.

Um zu verhindern, dass das Dialogfeld "Nicht behandelte Ausnahme" angezeigt wird, und solche Ausnahmen zum Protokollieren und / oder zum Bereitstellen eines eigenen Fehlerdialogs abzufangen, können Sie das Application.ThreadException-Ereignis vor dem Aufruf von Application.Run () in Ihrer Main () -Methode anhängen.

user95680
quelle
Vielen Dank für diesen Vorschlag. Ich habe dies zu spät erfahren. Ich habe es gerade in linqpad überprüft. Es funktioniert wie erwartet. Der Delegat lautet: void Form1_UIThreadException (Objektabsender, ThreadExceptionEventArgs t) Eine weitere gute Quelle zu diesem Thema ist richnewman.wordpress.com/2007/ 04/08 /… Für die allgemeine Behandlung nicht behandelter Ausnahmen: Das Ereignis AppDomain.UnhandledException gilt für nicht behandelte Ausnahmen, die von einem Nicht-Haupt-UI-Thread ausgelöst werden.
Zhaorufei
14

Alle hier veröffentlichten Ratschläge sind gut und es lohnt sich, sie zu beachten.

Eine Sache, auf die ich näher eingehen möchte, ist Ihre Frage: "Haben die Behandlung von Ausnahmen, die möglicherweise ausgelöst werden, einen Leistungseinbruch im Vergleich zum vorbeugenden Testen von Dingen wie der Existenz einer Datei auf der Festplatte?"

Die naive Faustregel lautet: "Try / Catch-Blöcke sind teuer." Das stimmt eigentlich nicht. Versuchen ist nicht teuer. Es ist der Haken, bei dem das System ein Ausnahmeobjekt erstellen und es mit dem Stack-Trace laden muss, das ist teuer. Es gibt viele Fälle, in denen die Ausnahme so außergewöhnlich ist, dass es vollkommen in Ordnung ist, den Code in einen Try / Catch-Block zu packen.

Wenn Sie beispielsweise ein Wörterbuch füllen, gehen Sie wie folgt vor:

try
{
   dict.Add(key, value);
}
catch(KeyException)
{
}

ist oft schneller als dies:

if (!dict.ContainsKey(key))
{
   dict.Add(key, value);
}

für jedes einzelne Element, das Sie hinzufügen, da die Ausnahme nur ausgelöst wird, wenn Sie einen doppelten Schlüssel hinzufügen. (LINQ-Aggregatabfragen tun dies.)

In dem Beispiel, das Sie gegeben haben, würde ich try / catch fast ohne nachzudenken verwenden. Nur weil die Datei beim Überprüfen vorhanden ist, bedeutet dies nicht, dass sie beim Öffnen vorhanden sein wird. Daher sollten Sie die Ausnahme trotzdem wirklich behandeln.

Zweitens, und ich denke noch wichtiger, es sei denn, a) Ihr Prozess öffnet Tausende von Dateien und b) die Wahrscheinlichkeit, dass eine Datei, die er zu öffnen versucht, nicht vorhanden ist, ist nicht trivial gering. Der Leistungseinbruch beim Erstellen der Ausnahme liegt nicht bei Ihnen. Ich werde es jemals bemerken. Wenn Ihr Programm versucht, eine Datei zu öffnen, versucht es im Allgemeinen nur, eine Datei zu öffnen. In diesem Fall ist das Schreiben von sichererem Code mit ziemlicher Sicherheit besser als das Schreiben des schnellstmöglichen Codes.

Robert Rossney
quelle
3
Im Fall von dict können Sie einfach dict[key] = value
Folgendes
9

Hier sind einige Richtlinien, denen ich folge

  1. Fail-Fast: Dies ist eher eine Richtlinie zur Ausnahmegenerierung. Führen Sie für jede Annahme, die Sie treffen, und für jeden Parameter, den Sie in eine Funktion einfügen, eine Überprüfung durch, um sicherzustellen, dass Sie mit den richtigen Daten beginnen und die Annahmen, die Sie treffen machen machen sind richtig. Typische Überprüfungen sind: Argument nicht null, Argument im erwarteten Bereich usw.

  2. Beim erneuten Werfen die Stapelverfolgung beibehalten - Dies bedeutet einfach, dass beim erneuten Werfen throw verwendet wird, anstatt eine neue Ausnahme () zu werfen. Wenn Sie der Meinung sind, dass Sie weitere Informationen hinzufügen können, können Sie die ursprüngliche Ausnahme als innere Ausnahme einschließen. Wenn Sie jedoch eine Ausnahme nur zum Protokollieren abfangen, verwenden Sie auf jeden Fall throw.

  3. Fangen Sie keine Ausnahmen ab, die Sie nicht behandeln können. Machen Sie sich also keine Sorgen über Dinge wie OutOfMemoryException, denn wenn sie auftreten, können Sie sowieso nicht viel tun.

  4. Verknüpfen Sie globale Ausnahmebehandlungsroutinen und stellen Sie sicher, dass Sie so viele Informationen wie möglich protokollieren. Bei Winforms werden sowohl die Appdomain- als auch die Thread-nicht behandelten Ausnahmeereignisse verknüpft.

  5. Die Leistung sollte nur berücksichtigt werden, wenn Sie den Code analysiert und festgestellt haben, dass er einen Leistungsengpass verursacht. Optimieren Sie standardmäßig die Lesbarkeit und das Design. Was Ihre ursprüngliche Frage zur Überprüfung der Dateiexistenz betrifft, würde ich sagen, dass es davon abhängt, ob Sie etwas dagegen tun können, dass die Datei nicht vorhanden ist. Dann überprüfen Sie diese andernfalls, wenn Sie nur eine Ausnahme auslösen, wenn die Datei vorhanden ist nicht da, dann verstehe ich den Punkt nicht.

  6. Es gibt definitiv Zeiten, in denen leere Fangblöcke erforderlich sind. Ich denke, Leute, die etwas anderes sagen, haben nicht an Codebasen gearbeitet, die sich über mehrere Releases hinweg entwickelt haben. Sie sollten jedoch kommentiert und überprüft werden, um sicherzustellen, dass sie wirklich benötigt werden. Das typischste Beispiel sind Entwickler, die try / catch verwenden, um Zeichenfolgen in Ganzzahlen zu konvertieren, anstatt ParseInt () zu verwenden.

  7. Wenn Sie erwarten, dass der Aufrufer Ihres Codes Fehlerbedingungen verarbeiten kann, erstellen Sie benutzerdefinierte Ausnahmen, die die unerwartete Situation detailliert beschreiben und relevante Informationen bereitstellen. Andernfalls halten Sie sich so weit wie möglich an die integrierten Ausnahmetypen.

Sijin
quelle
Was nützt es, sowohl die Appdomain als auch die nicht behandelten Thread-Ausnahmebehandlungsroutinen zu verknüpfen? Wenn Sie nur Appdomain.UnhandledException verwenden, ist das nicht die allgemeinste, die alles abfängt?
Sumpf
Meinten Sie damit, das Application.ThreadExceptionEreignis zu behandeln, als Sie auf das Ereignis "Thread-unbehandelte Ausnahme" verwiesen haben?
Jeff B
4

Ich mag die Philosophie, nichts zu fangen, was ich nicht zu handhaben beabsichtige, was auch immer die Handhabung in meinem speziellen Kontext bedeutet.

Ich hasse es, wenn ich Code sehe wie:

try
{
   // some stuff is done here
}
catch
{
}

Ich habe dies von Zeit zu Zeit gesehen und es ist ziemlich schwierig, Probleme zu finden, wenn jemand die Ausnahmen "isst". Ein Mitarbeiter, den ich hatte, tut dies und es trägt tendenziell zu einem stetigen Strom von Problemen bei.

Ich werfe erneut, wenn es etwas gibt, das meine bestimmte Klasse als Reaktion auf eine Ausnahme tun muss, aber das Problem muss herausgesprudelt werden, um jedoch die Methode zu nennen, bei der es passiert ist.

Ich denke, Code sollte proaktiv geschrieben werden und Ausnahmen sollten für Ausnahmesituationen gelten, um das Testen auf Bedingungen nicht zu vermeiden.

itsmatt
quelle
@Kiquenet Entschuldigung, ich war ein bisschen unklar. Was ich meinte: Machen Sie keine solchen leeren Fänge, sondern fügen Sie stattdessen einen Handler zu Dispatcher.UnhandledException hinzu und fügen Sie mindestens ein Protokoll hinzu, damit es nicht ohne Semmelbrösel gegessen wird :) Aber wenn Sie nicht die Anforderung einer stillen Komponente haben, sollten Sie immer Ausnahmen einschließen in Ihr Design. Wirf sie, wenn sie geworfen werden müssen, und mache klare Verträge für dich. Schnittstellen / API / jede Klasse.
LuckyLikey
4

Ich bin gerade auf dem Weg nach draußen, werde Ihnen aber einen kurzen Überblick darüber geben, wo Sie die Ausnahmebehandlung verwenden können. Ich werde versuchen, Ihre anderen Punkte anzusprechen, wenn ich zurückkomme :)

  1. Überprüfen Sie explizit alle bekannten Fehlerzustände *
  2. Fügen Sie einen Versuch / Fang um den Code hinzu, wenn Sie nicht sicher sind, ob Sie alle Fälle bearbeiten konnten
  3. Fügen Sie einen Try / Catch um den Code hinzu, wenn die von Ihnen aufgerufene .NET-Schnittstelle eine Ausnahme auslöst
  4. Fügen Sie einen Try / Catch um den Code hinzu, wenn er einen Komplexitätsschwellenwert für Sie überschreitet
  5. Fügen Sie einen Versuch / Fang um den Code hinzu, wenn für eine Überprüfung der Gesundheit: Sie behaupten, dass dies niemals passieren sollte
  6. In der Regel verwende ich keine Ausnahmen als Ersatz für Rückkehrcodes. Dies ist in Ordnung für .NET, aber nicht für mich. Ich habe jedoch Ausnahmen (hehe) von dieser Regel, dies hängt auch von der Architektur der Anwendung ab, an der Sie arbeiten.

*Im Rahmen des Zumutbaren. Es ist nicht erforderlich zu überprüfen, ob beispielsweise ein kosmischer Strahl Ihre Daten getroffen hat, wodurch ein paar Bits umgedreht werden. Zu verstehen, was "vernünftig" ist, ist eine erworbene Fähigkeit für einen Ingenieur. Es ist schwer zu quantifizieren und dennoch leicht zu verstehen. Das heißt, ich kann leicht erklären, warum ich in einem bestimmten Fall einen Versuch / Fang verwende, aber es fällt mir schwer, einen anderen mit demselben Wissen zu erfüllen.

Ich neige dazu, mich von stark ausnahmebasierten Architekturen fernzuhalten. try / catch hat keinen Leistungseinbruch als solchen. Der Treffer tritt ein, wenn die Ausnahme ausgelöst wird und der Code möglicherweise mehrere Ebenen des Aufrufstapels durchlaufen muss, bevor etwas damit umgehen kann.

pezi_pink_squirrel
quelle
4

Die goldene Regel, an die man sich halten wollte, ist, die Ausnahme so nah wie möglich an der Quelle zu behandeln.

Wenn Sie eine Ausnahme erneut auslösen müssen, versuchen Sie, sie hinzuzufügen. Das erneute Auslösen einer FileNotFoundException hilft nicht viel, aber das Auslösen einer ConfigurationFileNotFoundException ermöglicht es, sie irgendwo in der Kette zu erfassen und zu bearbeiten.

Eine andere Regel, die ich zu befolgen versuche, ist, try / catch nicht als eine Form des Programmflusses zu verwenden. Daher überprüfe ich Dateien / Verbindungen, stelle sicher, dass Objekte initiiert wurden usw., bevor ich sie verwende. Try / Catch sollte für Ausnahmen sein, Dinge, die Sie nicht kontrollieren können.

Wenn Sie bei einem leeren catch-Block etwas Wichtiges in dem Code tun, der die Ausnahme generiert hat, sollten Sie die Ausnahme mindestens erneut auslösen. Wenn der Code, der die Ausnahme ausgelöst hat, keine Konsequenzen hat, warum nicht, warum haben Sie ihn überhaupt geschrieben?


quelle
Diese "goldene Regel" ist bestenfalls willkürlich.
Evan Harper
3

Sie können das ThreadException-Ereignis abfangen.

  1. Wählen Sie im Projektmappen-Explorer ein Windows-Anwendungsprojekt aus.

  2. Öffnen Sie die generierte Datei Program.cs, indem Sie darauf doppelklicken.

  3. Fügen Sie die folgende Codezeile oben in die Codedatei ein:

    using System.Threading;
  4. Fügen Sie in der Main () -Methode Folgendes als erste Zeile der Methode hinzu:

    Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
  5. Fügen Sie unter der Main () -Methode Folgendes hinzu:

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        // Do logging or whatever here
        Application.Exit();
    }
  6. Fügen Sie Code hinzu, um die nicht behandelte Ausnahme im Ereignishandler zu behandeln. Jede Ausnahme, die an keiner anderen Stelle in der Anwendung behandelt wird, wird vom obigen Code behandelt. In den meisten Fällen sollte dieser Code den Fehler protokollieren und dem Benutzer eine Meldung anzeigen.

Aktualisierung: https://blogs.msmvps.com/deborahk/global-exception-handler-winforms/

Omid-RH
quelle
@ Kiquenet Entschuldigung, ich weiß nicht über VB
Omid-RH
2

Ausnahmen sind teuer, aber notwendig. Sie müssen nicht alles in einen Try-Catch einwickeln, aber Sie müssen sicherstellen, dass Ausnahmen immer irgendwann abgefangen werden. Vieles davon hängt von Ihrem Design ab.

Werfen Sie nicht erneut, wenn das Auslösen der Ausnahme genauso gut funktioniert. Lassen Sie Fehler niemals unbemerkt.

Beispiel:

void Main()
{
  try {
    DoStuff();
  }
  catch(Exception ex) {
    LogStuff(ex.ToString());
  }

void DoStuff() {
... Stuff ...
}

Wenn DoStuff schief geht, soll es trotzdem auf Kaution gehen. Die Ausnahme wird auf main geworfen und Sie sehen den Ereigniszug in der Stapelspur von ex.

Echostorm
quelle
1

Wann sollte ich eine Ausnahme erneut auslösen?

Überall, aber Endbenutzermethoden ... wie Button-Click-Handler

Sollte ich versuchen, einen zentralen Fehlerbehandlungsmechanismus zu haben?

Ich schreibe eine Protokolldatei ... ziemlich einfach für eine WinForm-App

Haben die Behandlung von Ausnahmen, die möglicherweise ausgelöst werden, einen Leistungseinbruch im Vergleich zu vorbeugenden Tests, z. B. ob eine Datei auf der Festplatte vorhanden ist?

Ich bin mir nicht sicher, aber ich glaube, es ist eine gute Praxis, Ausnahmen zuzulassen ... Ich meine, Sie können fragen, ob eine Datei existiert und ob sie keine FileNotFoundException auslöst

Sollte der gesamte ausführbare Code in Try-Catch-finally-Blöcken eingeschlossen sein?

ja

Gibt es Zeiten, in denen ein leerer Fangblock akzeptabel sein könnte?

Ja, nehmen wir an, Sie möchten ein Datum anzeigen, aber Sie haben keine Ahnung, wie dieses Datum gespeichert wurde (TT / MM / JJJJ, MM / TT / JJJJ usw.). Sie versuchen, es zu analysieren, aber wenn es fehlschlägt, fahren Sie einfach fort. Wenn es für Sie irrelevant ist ... Ich würde ja sagen, das gibt es

Sebagomez
quelle
1

Das einzige, was ich sehr schnell gelernt habe, war, absolut jeden Codeabschnitt, der mit etwas außerhalb des Programmflusses (dh Dateisystem, Datenbankaufrufe, Benutzereingaben) interagiert , mit Try-Catch-Blöcken einzuschließen . Try-Catch kann zu Leistungseinbußen führen, aber normalerweise fällt es an diesen Stellen in Ihrem Code nicht auf und macht sich mit Sicherheit bezahlt.

Ich habe leere Catch-Blöcke an Stellen verwendet, an denen der Benutzer möglicherweise etwas tut, das nicht wirklich "falsch" ist, aber eine Ausnahme auslösen kann. Ein Beispiel, das mir in den Sinn kommt, ist eine GridView, wenn der Benutzer auf den grauen Platzhalter doppelklickt Zelle oben links löst das CellDoubleClick-Ereignis aus, aber die Zelle gehört nicht zu einer Zeile. In diesem Fall tun Sie nicht Ich muss wirklich eine Nachricht posten, aber wenn Sie sie nicht abfangen, wird dem Benutzer ein nicht behandelter Ausnahmefehler angezeigt.

BKimmel
quelle
1

Beim erneuten Auslösen einer Ausnahme wird das Schlüsselwort selbst ausgelöst. Dadurch wird die abgefangene Ausnahme ausgelöst, und die Stapelverfolgung kann weiterhin verwendet werden, um festzustellen, woher sie stammt.

Try
{
int a = 10 / 0;
}
catch(exception e){
//error logging
throw;
}

Wenn Sie dies tun, endet die Stapelverfolgung in der catch-Anweisung. (vermeide dies)

catch(Exception e)
// logging
throw e;
}
Brad8118
quelle
Gilt dies immer noch für die neuesten Versionen von .NET Framework und .NET Core?
LuckyLikey
1

Nach meiner Erfahrung habe ich es für angebracht gehalten, Ausnahmen zu fangen, wenn ich weiß, dass ich sie erstellen werde. In Fällen, in denen ich in einer Webanwendung bin und eine Response.Redirect-Datei ausführe, weiß ich, dass ich eine System.ThreadAbortException erhalte. Da es beabsichtigt ist, habe ich nur einen Haken für den bestimmten Typ und schlucke ihn einfach.

try
{
/*Doing stuff that may cause an exception*/
Response.Redirect("http:\\www.somewhereelse.com");
}
catch (ThreadAbortException tex){/*Ignore*/}
catch (Exception ex){/*HandleException*/}
Jeff Keslinke
quelle
1

Ich stimme der Regel zutiefst zu:

  • Lassen Sie Fehler niemals unbemerkt.

Der Grund ist, dass:

  • Wenn Sie den Code zum ersten Mal aufschreiben, verfügen Sie höchstwahrscheinlich nicht über die vollständigen Kenntnisse des 3-Parteien-Codes, der .NET FCL-Bibliothek oder der neuesten Beiträge Ihrer Mitarbeiter. In Wirklichkeit können Sie das Schreiben von Code erst ablehnen, wenn Sie alle möglichen Ausnahmen gut kennen. So
  • Ich stelle ständig fest, dass ich try / catch (Exception ex) verwende, nur weil ich mich vor unbekannten Dingen schützen möchte, und wie Sie bemerkt haben, fange ich Exception, nicht die spezifischeren wie OutOfMemoryException usw. Und mache immer die Ausnahme von ForceAssert.AlwaysAssert (false, ex.ToString ()) zu mir (oder zur Qualitätssicherung) gebracht werden;

ForceAssert.AlwaysAssert ist meine persönliche Art von Trace.Assert, nur unabhängig davon, ob das DEBUG / TRACE-Makro definiert ist.

Der Entwicklungszyklus vielleicht: Ich habe den hässlichen Assert-Dialog bemerkt oder jemand anderes hat sich bei mir darüber beschwert, dann komme ich zum Code zurück und finde den Grund heraus, die Ausnahme auszulösen und zu entscheiden, wie sie verarbeitet werden soll.

Auf diese Weise kann ich MEINEN Code in kurzer Zeit aufschreiben und mich vor unbekannten Domänen schützen, aber immer bemerkt werden, wenn die abnormalen Dinge passiert sind, auf diese Weise wurde das System sicherer und sicherer.

Ich weiß, dass viele von Ihnen mir nicht zustimmen werden, weil ein Entwickler jedes Detail seines Codes kennen sollte, ehrlich gesagt bin ich auch früher Purist. Aber heutzutage habe ich gelernt, dass die oben genannte Politik pragmatischer ist.

Für WinForms-Code ist eine goldene Regel, die ich immer befolge:

  • Versuchen Sie immer, Ihren Ereignishandlercode zu erfassen (Ausnahme)

Dadurch wird geschützt, dass Ihre Benutzeroberfläche immer verwendbar ist.

Bei Leistungseinbußen tritt eine Leistungsbeeinträchtigung nur dann auf, wenn der Code den Fang erreicht. Die Ausführung des Versuchscodes ohne die tatsächlich ausgelöste Ausnahme hat keine wesentlichen Auswirkungen.

Ausnahmen sollten mit geringer Wahrscheinlichkeit geschehen, sonst sind es keine Ausnahmen.

zhaorufei
quelle
-1

Sie müssen an den Benutzer denken. Der Anwendungsabsturz ist der letztewas der Benutzer will. Daher sollte jede Operation, die fehlschlagen kann, einen try catch-Block auf der UI-Ebene haben. Es ist nicht erforderlich, try catch in jeder Methode zu verwenden, aber jedes Mal, wenn der Benutzer etwas tut, muss er in der Lage sein, generische Ausnahmen zu verarbeiten. Das befreit Sie keineswegs davon, im ersten Fall alles zu überprüfen, um Ausnahmen zu vermeiden, aber es gibt keine komplexe Anwendung ohne Fehler, und das Betriebssystem kann leicht unerwartete Probleme hinzufügen. Daher müssen Sie das Unerwartete antizipieren und sicherstellen, dass ein Benutzer eine verwenden möchte Betrieb gibt es keinen Datenverlust, da die App abstürzt. Es ist nicht erforderlich, Ihre App jemals abstürzen zu lassen. Wenn Sie Ausnahmen abfangen, befindet sie sich niemals in einem unbestimmten Zustand und der Benutzer wird IMMER durch einen Absturz belästigt. Auch wenn die Ausnahme auf der obersten Ebene liegt, Wenn Sie nicht abstürzen, kann der Benutzer die Ausnahme schnell reproduzieren oder zumindest die Fehlermeldung aufzeichnen und Ihnen so bei der Behebung des Problems helfen. Sicherlich viel mehr als nur eine einfache Fehlermeldung zu erhalten und dann nur Windows-Fehlerdialoge oder ähnliches zu sehen.

Deshalb darfst du NIEMALS nur eingebildet sein und denken, deine App hat keine Fehler, das ist nicht garantiert. Und es ist ein sehr kleiner Aufwand, einige Try-Catch-Blöcke um den entsprechenden Code zu wickeln und eine Fehlermeldung anzuzeigen / den Fehler zu protokollieren.

Als Benutzer bin ich auf jeden Fall ernsthaft sauer, wenn eine Augenbraue oder eine Office-App oder was auch immer abstürzt. Wenn die Ausnahme so hoch ist, dass die App nicht fortgesetzt werden kann, ist es besser, diese Meldung anzuzeigen und dem Benutzer mitzuteilen, was zu tun ist (Neustart, Behebung einiger Betriebssystemeinstellungen, Meldung des Fehlers usw.), als einfach abzustürzen und fertig.

Terry Bogard
quelle
Können Sie versuchen, Antworten weniger wie ein Scherz zu schreiben und stattdessen genauer zu sein und Ihren Standpunkt zu beweisen? Schauen Sie sich auch an, wie man antwortet .
LuckyLikey