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!
quelle
ApplicationException
für die Fehlerfälle, die die Anwendung vernünftigerweise unterscheiden und behandeln sollte.Hier gibt es einen ausgezeichneten CodeProject-Artikel . Hier einige Highlights:
quelle
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.
quelle
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:
ist oft schneller als dies:
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.
quelle
dict[key] = value
Hier sind einige Richtlinien, denen ich folge
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.
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.
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.
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.
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.
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.
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.
quelle
Application.ThreadException
Ereignis zu behandeln, als Sie auf das Ereignis "Thread-unbehandelte Ausnahme" verwiesen haben?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:
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.
quelle
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 :)
*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.
quelle
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
Sie können das ThreadException-Ereignis abfangen.
Wählen Sie im Projektmappen-Explorer ein Windows-Anwendungsprojekt aus.
Öffnen Sie die generierte Datei Program.cs, indem Sie darauf doppelklicken.
Fügen Sie die folgende Codezeile oben in die Codedatei ein:
Fügen Sie in der Main () -Methode Folgendes als erste Zeile der Methode hinzu:
Fügen Sie unter der Main () -Methode Folgendes hinzu:
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/
quelle
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:
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.
quelle
Überall, aber Endbenutzermethoden ... wie Button-Click-Handler
Ich schreibe eine Protokolldatei ... ziemlich einfach für eine WinForm-App
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
ja
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
quelle
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.
quelle
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.
Wenn Sie dies tun, endet die Stapelverfolgung in der catch-Anweisung. (vermeide dies)
quelle
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.
quelle
Ich stimme der Regel zutiefst zu:
Der Grund ist, dass:
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:
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.
quelle
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.
quelle