Sunday 26 November 2017

Gethrforexception Ioexception Wird Niemals Geworfen


Ich erstelle eine Datei in C (.NET Web Service) und möchte keine vorhandene Datei überschreiben. Der Weg scheint zu sein, um ein FileStream mit FileMode. CreateNew Set zu konstruieren. Es gibt eine Ausnahme, wenn die Datei existiert. Aber wie erkenne ich diese Ausnahme im Gegensatz zu anderen möglichen Ausnahmen durch die Erstellung der Datei geworfen Die Dokumentation auf msdn. microsoft/en-us/library/47ek66wy. aspx listet diesen Fall als eine IOException, die klar ist vage wie andere Dinge dazu führen können . Ist die Antwort hier, dass ich fangen IOException und dann nur tun, ein File. Exists gefragt, 9. Februar um 23.26 Ich denke, die File. Exists-Methode ist die eleganteste. Sie können spielen mit Reflexion zu versuchen und erraten die Ursache, aber es ist nicht wert. Es ist möglich, dass InnerException auf etwas Unterscheidendes eingestellt ist. Ist es null Und die Message-Eigenschaft sollte beschreiben (in Englisch oder was auch immer Sie mit) was genau passiert ist. Unter Berufung auf die Zeichenfolge zurückgegeben von Message ist auch keine gute Idee. Ich mag Ihre Idee am besten ehrlich zu sein. Hmm, ich sehe Ihre Sorge. Ich don39t wissen, wie sicherzustellen, keine Lücke zwischen, wenn Sie für die Datei zu überprüfen und wenn Sie es erstellen. Obwohl es spröde scheint, glaube ich nicht, dass die Ausnahmemeldung von CreateNew wahrscheinlich ändern wird. Wenn Sie absolut keine Lücke brauchen, I39d nur die Nachricht zu vergleichen, sondern stellen Sie sicher, dass Sie diese gut dokumentieren. Beachten Sie, dass Fangausnahmen als Teil Ihrer regelmäßigen Ausführung nicht empfohlen werden. Daher schlage ich vor, einen File. Exists UND einen try-catch zu tun, der die IOException39s Mitteilung überprüft. Auf diese Weise werden in 99 Fällen die File. Exists Sie ausreichend informieren. Ndash Daryl Feb 14 12 at 20: 56 Ich habe einige IO-Code, der einen Stream liest innerhalb eines try..catch. Es fängt IOException und ruft System. Runtime. InteropServices. Marshal. GetHRForException () innerhalb der catch, in einem Versuch, verschiedene Aktionen auf der Grundlage der HResult nehmen. So etwas wie dieses: Aber diesen Code in ASP. NET mit trustmedium ausführen, bekomme ich diese Ausnahme: Ein paar Fragen: Ich denke, die Ausnahme ist aufgetreten, weil GetHRForException ruft in nicht verwalteten Code, der nicht in Medium Vertrauen erlaubt ist. Korrigieren Diese Ausnahme wird geworfen, nicht zum Zeitpunkt der Ausführung von GetHRForException, aber zum Zeitpunkt der Methode wird JITed - Correct (Der stacktrace zeigt meine Methode, aber ich bin 99 sicher, dass eine IO-Ausnahme nicht aufgetreten ist) Wenn ja, Gibt es eine Möglichkeit für mich, das Verhalten in einer partiellen Vertrauensumgebung variieren, so dass ich nicht rufen Sie die GetHRForException (nicht verwalteten Code), wo es nicht erlaubt ist Mit anderen Worten, wie kann ich dem JIT erlauben, zum Zeitpunkt der Kompilierung erfolgreich zu sein, während auch Auswertung zur Laufzeit, ob der Code GetHRForException () aufrufen sollte So etwas wie dies: Ich denke, es gibt einen Laufzeitmechanismus für den Test, wenn Berechtigungen verfügbar sind, aber havent in der Lage, es zu finden. BEARBEITEN. Ist dieser Blog-Artikel die Antwort ShawnFa von Microsoft sagt, dass Sie nicht versuchen können. Catch (SecurityException) um eine Methode, die durch einen LinkDemand geschützt ist. Wenn MethodA () MethodB () aufruft und MethodB () mit LinkDemand für vollständige Vertrauenswürdigkeit markiert wird, wird der LinkDemand überprüft, wobei MethodA Jited ist. Deshalb, um die SecurityException zu vermeiden, muss ich Marshal. GetHRForException in eine separate Methode extrahieren. Ist das korrekt Auf meinen Code angewendet, kann MethodA () der Code sein, der Read aufruft und dann im catch versucht, GetHRForException () aufzurufen. GetHRForException ist MethodB (). Der LinkDemand wird ausgewertet, wenn MethodeA () JITd ist. (Dieser LinkDemand schlägt in meinem Medium vertrauen ASP. NET-Szenario). Wenn ich die GetHRForException in eine neue Methode MethodC () verschieben und MethodC () nur bedingt aufrufen, nachdem eine imperative permission. Demand () erfolgreich ist, sollte ich theoretisch die SecurityException an der JIT-Zeit vermeiden können, da MethodC () sein wird JITd nur, nachdem die permission. Demain () erfolgreich ist. Gestellt Jul 12 09 am 14:20 Die erforderliche Methode ist SecurityPermission. IsUnrestricted (). Es gibt eine true oder false, die angibt, ob die Berechtigung erlaubt ist oder nicht. Es erfordert keine Berechtigung, ebenso wie SecurityPermission. Demand (). Ich benutze IsUnresticted mit SecurityPermissionFlag. UnmanagedCode zu sehen, wenn die Assembly darf nicht verwalteten Code aufrufen, und rufen Sie den nicht verwalteten Code nur, wenn erlaubt. Es gibt einen zusätzlichen Twist. Der JIT-Compiler prüft beim Kompilieren einer Methode nach CodeAccessPermission LinkDemands auf jede Methode, die als die zu kompilierende Methode bezeichnet wird. Marshal. GetHRForException () ist mit einem LinkDemand markiert. Daher wird meine Methode, die Marshal. GetHRForException () aufruft, eine uncatchable SecurityException zum Zeitpunkt der JIT-Kompilierung werfen, wenn in einer beschränkten Umgebung wie ASP. NET mit mittlerer Vertrauenswürdigkeit ausgeführt wird. Daher muss man nicht JIT die Methode, die Marshal. GetHRForException () in diesem Fall, was bedeutet, dass ich ausbrechen müssen, Marshal. GetHRForException () in eine separate Methode in meinem Code, der aufgerufen wird (und somit JITted) nur dann, wenn UnmanagedCode ist uneingeschränkt. Heres einige Beispiel-Code: beantwortet Jul 20 09 at 17:56 Ja - mittlere Vertrauen wird nicht zulassen, Anrufe in nicht verwalteten Code. Die einzige Vertrauensstufe, die es zulässt, ist volles Vertrauen. Es hängt davon ab, ob. CAS-Anforderungen können zur Laufzeit stattfinden, aber die Hosting-Umgebung kann auch auf eine Wanderung gehen und suchen Dinge, die sie nicht tun kann. Sie können testen, um festzustellen, ob Sie einen nicht verwalteten Code anrufen können, indem Sie eine CAS-Anfrage mit einer Instanz von SecurityPermission verwenden. Der Code, um eine CAS-Nachfrage sieht aus wie dies beantwortet. Dies deckt Teil b von Q3 ab. Aber was ist mit Teil a wie bekomme ich die JIT-Kompilierung zum Erfolg Kann ich meine Methode mit einem Sicherheitsattribut oder markieren. Denken Sie daran, meine Theorie ist, dass der SecurityPermission-Fehler nicht zur Laufzeit geschieht, es geschieht während JIT - und ich denke, Sie bestätigt, dass dies möglich ist. Also die Frage ist, wie schreibe ich den Code, damit die JIT zu kompilieren. Ndash Cheeso Es sollte zur Laufzeit geschehen, sonst würde die Montage wouldn39t sogar laden - und dafür muss die Assembly muss als die Erlaubnis erforderlich markiert werden. Selbst dann that39s wohl ein Laufzeit-Check, wie es bei Montage Belastung, die zur Laufzeit sein könnte passieren wird. Ndash blowdart Unterschiedliche Art der Überprüfung, Link-Anforderungen sind Attribute auf eine Methode, und werden tatsächlich bei JIT-Zeit überprüft. It39s ziemlich viel vom Rahmen selbst verwendet und es ist selten, es außerhalb der CLR-Quelle zu sehen. Was ich demonstrieren ist eine zwingende Forderung, nicht ein deklarativer wie ein SecurityPermission (SecurityAction. LinkDemand, uneingeschränkte true) ndash blowdart 12:10:10 Page 2 QUALITÄTSMASSE 8: Ausnahmen für wiederherstellbare Zustände Überlastungszeitraum 120 - April 2014 Programmierung Themen Autor: Matthew Wilson Zu viele Programme behandeln Ausnahmen falsch. Matthew Wilson schlägt praktische Schritte, um Ihren Code zu verbessern. In dieser Tranche wende ich mich an die Verwendung von Ausnahmen mit (potenziell) erstattungsfähigen Bedingungen und untersuche, welche Merkmale von Ausnahmen notwendig sind, um die Erholung zu unterstützen. Ich überlege auch, was getan werden kann, wenn die Informationen zur Unterstützung Recovery-Entscheidungen nicht zur Verfügung gestellt wird, und stellen Sie eine neue Open-Source-Bibliothek, Quench. NET. Die im Umgang mit solchen Situationen hilft. (Ich beginne mit einer Demonstration einer früheren Behauptung: Ausnahmen sind im Grunde nichts weiter als ein Flußkontrollmechanismus, und wir sollten vorsichtig sein, es nicht zu vergessen.) Einleitung Dies ist die achte Tranche von Quality Matters, die vierte Auf Ausnahmen, und die erste, da ich in der April 2013 Ausgabe von Überlastung, dass ich sowohl regelmäßig und häufig von hier auf in. Die Ursache war so skizzenhaft 20102013 war, dass Id auf eine sehr schwere Verlobung als Experte Zeuge auf Ein allumfassendes intellektuelles Copyright Fall. Seither arbeite ich als Entwicklungsberater / Manager / Software-Architekt für einen großen australischen Online-Händler und arbeite an einer Inbetriebnahme, deren letzterer mich nun vollzeit beschäftigt (was 70 Wochen dauert) Weg dieser Dinge). Beide dieser späteren Aktivitäten haben sich vor allem mit den Fragen der Software-Qualität im Allgemeinen und der Exception-Handhabung auseinandergesetzt, und so komme ich wieder voller Ideen (wenn auch nicht ganz frisch und voller Energie), und ich beabsichtige, wieder zu schreiben Und sogar die ungeraden Veröffentlichung zu erfüllen. In dieser Rate betrachte ich ein wenig mehr darüber, was Ausnahmen sind, wie sie in erstattungsfähigen Situationen verwendet werden. Und welche Merkmale gefordert werden, um die Diskriminierung zwischen erstattungsfähigen und praktisch nicht wiederkehrbaren Bedingungen zu erleichtern. Dies ist ein viel größeres Sub-Subjekt als Id jemals erwartet, wenn ich auf das Thema begann, die vor einigen Jahren, und ich jetzt erwarten, dass die Abdeckung Erholung alleine für mehrere Raten. Ich werde keine weiteren Vorhersagen darüber hinaus. Ich untersuche Probleme sowohl mit dem Design und der Verwendung von Ausnahmen (und Ausnahme-Hierarchien) bei der Berichterstattung Misserfolg, und das Missverständnis in Bezug auf und Missbrauch von Ausnahmen ausgelöst durch Programmierer. Ich beende durch die Einführung einer neuen Open-Source-Bibliothek, Quench. NET. Die dazu bestimmt ist, die behaarte Aufgabe der Nachrüstung von Fixes auf die erstere zu unterstützen und diese zu tolerieren. Exception-class Split-Persönlichkeitssyndrom System. ArgumentException dient der doppelten Auslastung, sowohl als spezifische Ausnahmeklasse, die zum Berichten von bestimmten Bedingungen verwendet wird, als auch als Basisklasse einer Gruppe von (argumentbezogenen) Ausnahmeklassen. Diese Überlastung des Zwecks wirft zwei Probleme auf. Erstens bedeutet dies, dass man darauf achten muss, die benötigten abgeleiteten Typen in einen Mehrfachverriegelungsblock einzuschließen, um die spezifischen Typen zu erfassen und korrekt zu bestellen (die meisten spezifischen Klassen zuerst). Zweitens und noch wichtiger meiner Meinung nach, macht es die Absicht (der Art) zweideutig: Bin ich fangen ArgumentException als ArgumentException. Oder bin ich fangen alle Argument Ausnahmen und Warum hat Bedingung A seinen eigenen spezifischen Exception-Typ (z. B. System. ArgumentOutOfRangeException) und Bedingung B nicht und verwendet stattdessen die allgemeine ArgumentException) Warum haben wir zum Beispiel keine ArgumentInvalidValueException. Vielleicht sogar eine ArgumentEmptyException. Und so weiter Das gleiche Problem tritt bei System. IO. IOException und seine parsimonious stabil von abgeleiteten Klassen Sie eine FileNotFoundException und eine DirectoryNotFoundException ok, aber wenn einige Fehler, die beim Zugriff auf einen Netzwerkpfad auftreten, eine plan-alte IOException auftreten. Anders als Argumentausnahmen sind I / O-Ausnahmen wahrscheinlich an Entscheidungen über die Wiederherstellbarkeit beteiligt, so dass dieser Designfehler viel gravierendere Konsequenzen hat. Präludium der pedantischen contrarian Wie ich am Ende der ersten Tranche am Ausnahmen zum Ausdruck gebracht habe, sind Ausnahmen kein Fehlerberichterstattungsmechanismus per se, sondern alleine ein intrinsischer Mechanismus zur Durchflusskontrolle. Es kommt einfach so vor, dass sie als Fehlerreporting-Mechanismus verwendet werden können (und in der Tat konzipiert wurden und sein sollten). Aber nur, weil das ist, wie sie oft verwendet, bedeutet nicht, dass das ist, was sie sind. Ive überrascht worden in letzter Zeit, gerade wie hart ein Konzept dieses ist, um Programmierern zu übermitteln, also Im gehend, es nach Hause mit einer einfachen Probe (siehe Listing 1) zu hämmern. Nun, es gibt eine ganze Menge von Vereinfachungen und Kompromisse in diesem Code für pädagogische Zwecke (wie die Verwendung von Private-Set Eigenschaften anstelle von unveränderlichen Bereichen yuck) seit Im versuchen, meinen Punkt auf dem kleinsten Raum zu machen. (Die Tatsache, dass ich mich nicht zwingen kann, den Fehlerbehandlungscode zu überspringen, fügt der offensichtlichen Größe dieses einfachen Codes sehr viel hinzu, aber ich hoffe, Sie verstehen, warum ich das nicht tun kann, sanfter Leser.) Ich glaube an seine Selbstverständlichkeit, dass die FindCompletionException Typ wird ausschließlich innerhalb des normativen Verhaltens des Programms verwendet. Es wird nicht verwendet, um einen Fehler anzuzeigen, sondern wird spezifisch verwendet, um anzuzeigen, dass das Programm sein Ziel erreicht hat, das Ort, die Größe und die Modifikationszeit der ersten Datei, die mit dem gegebenen Namen übereinstimmt, zu finden und zu berichten. Sollte ich mich dazu entschließen, könnte ich diese Taktik im realen Produktionscode anwenden, und das Programm wäre in keiner Weise für die barocke Art ihrer Umsetzung schlecht geformt. (Ich bekenne, dass ich Ausnahmen als eine kurzschneidende normative Flucht aus der tiefen Rekursion verwendet habe, aber es war eine sehr, sehr lange Zeit her, als ich nur ein Neophyten-C-Programmierer war, gerade aus der Schule für Scham.) Bitte beachten Sie , Immer noch sanfte Leser, dass Im nicht sagen, das ist ein gutes Design, oder eine gute Nutzung von Ausnahmen in der Tat, Im sagen, ihre schlechte Design und eine dumme Verwendung von Ausnahmen. Aber es illustriert, was möglich ist, und in einer Weise, die nicht eine Perversion der Sprache oder Laufzeit. Da ich unfähig geworden bin, Code zu schreiben, der wissentlich falsch / unzulänglich ist, vor allem in einer artikelübergreifenden Qualität, habe ich perforce einen catch (Exception) - Handler enthalten, um mit fehlerhaften Bedingungen umzugehen (wie zB die Angabe eines ungültigen Verzeichnisses). Infolgedessen veranschaulicht dieses Programm nicht nur meinen Hauptpunkt, daß Ausnahmen ein Flußkontrollmechanismus sind. Sondern auch eine sekundäre Veranschaulichung, dass die Verwendung der Ausnahmen in diesem Fall überlastet ist, tat sowohl das kurzschließende normative Verhalten als auch das Fehlverhalten. Wäre ich so dumm, könnte ich weiter in Überlastung der Bedeutung für die Verwendung von Ausnahmen durch Verzicht auf die if - Anweisung und stattdessen Fang IndexOutOfRangeException und die Ausstellung der USAGE-Beratung gibt es, wie in Listing 2.Ach gesehen, genau diese Perversion In der Produktion Code vor kurzem. Die Probleme einer solchen Auswahl werden im Detail in QM5 zusammengefasst: IndexOutOfRangeException wird von vielen Programmierern genommen, um ein Hinweis auf Programmierfehler, nicht eine gültige Laufzeitbedingung zu sein, und angesichts der Tatsache, dass In einem komplexeren Programm kann es eine bona fide Codierung geben Fehler, der IndexOutOfRangeException ausfällt. Die dann (falsch) behandelt wird, um den überraschten Benutzer darüber zu informieren, dass er das Programm missbraucht hat. Daher würden wir eine Konflikte (und Verwirrung) zwischen drei Bedeutungen der Verwendung von Ausnahmen sehen: für die Kurzschlussverarbeitung (normativ) für die Berichterstattung des Laufzeitfehlers (erstattungsfähig und, wie in diesem Fall, praktisch nicht wiederherstellbar) und für die Berichterstattung des Programmierers Fehler (fehlerhaft). Wiederherstellbar Als wir uns mit dem Umgang mit praktisch nicht behebbaren Bedingungen QM5 beschäftigten, nutzten wir zwei Aspekte der Ausnahmen, die verwendet wurden, um diese Bedingungen zu melden. Zuerst verließ man sich auf den Typ der Exception, um allgemein den breiten Sweep des Fehlers anzuzeigen: a std :: badalloc wurde als out-of-memory Bedingung im Laufzeithaufen interpretiert a clasp :: claspexception zeigte an, dass ein ungültiges Befehlszeilen - Line-Argument (Kombination) vom Benutzer angegeben wurde, hat eine recls :: reclsexception angegeben, dass eine Dateisystem-Suchoperation fehlgeschlagen ist. Eine zweite Ebene der Interpretation wurde dann in einigen dieser Handler zu informieren (der Benutzer, über kontingente Berichte der Administrator / Entwickler / Power-User über Diagnose-Log-Anweisungen) die Art des Fehlers durch die Verwendung von State-Attributen der Ausnahme Types: die nicht erkannte Befehlszeilenoption / flag für eine claspexception das fehlende / unzugängliche Element für eine reclsexception. Informationen können von der Website des Versagens auf der Website der Handhabung auf drei Arten übertragen werden. Zwei sind offensichtlich: Typ und Zustand der dritte ist Kontext. Die so offensichtlich sein kann, um der Beobachtung zu entgehen. Typ, Zustand und Kontext In den in QM5 untersuchten Beispielen wurde nur der Typ verwendet, um zu unterscheiden, wie die praktisch nicht wiederherstellbaren Bedingungen behandelt werden sollen, und nur insofern, als er feststellte, welche typspezifischen Zustandsinformationen in der Diagnoseprotokollanweisung und (Oder in einigen Fällen statt dessen) die von jeder C-Exception-Klasse (die von std :: exception abgeleitet wird) bereitgestellten what () - Messaging-Informationen. Handhabung (potenziell) wiederherstellbare Bedingungen erfordert mehr als diese einfache und etwas vage Rezept. Dies liegt daran, dass eine Entscheidung durch einen Teil der Entscheidungs-Software von uns Menschen kodiert werden muss, und da (nicht fehlerbehaftete) Software nur tut, was wir tun es tun (aber indirekt und / oder inchoately), wir gehen Um Informationen mit einer ganzen Reihe von anspruchsvollen Eigenschaften zu benötigen. Bevor ich sie definiere und ich nicht eine vollwertige Definition bis zu einer späteren Rate zur Verfügung stellen möchte, möchte ich ein Beispiel betrachten, das von der neuen praktischen Erfahrung gezeichnet wird, die nette Ausgaben des Typs illustriert. Bundesland . Und Kontext. Betrachten Sie den C-Code in Listing 3, vereinfacht für pädagogischen Zweck. Es gibt vier wichtige normative Aktionen zu berücksichtigen: Konstruieren Sie eine Instanz des Ports (System. IO. Ports. SerialPort) Öffnen Sie die Port-Instanz Schreiben Sie die Anforderung an den Port Lesen Sie die Antwort vom Port. Der Konstruktor von System. IO. Ports. SerialPort hat die folgende Signatur: Open () hat keine Parameter und einen Rückgabetyp void. Read () und Write () haben die folgenden Signaturen: Es gibt zahlreiche Möglichkeiten, wie dieser Satz von Anweisungen fehlschlagen kann, von Programmierfehler bis hin zu Hardwarefehlern, und sie können in eine von zwei Fehlerklassen fallen: inakzeptable Argumente . Und Laufzeitfehler. Unannehmbare Argumente Diese Klasse von Fehlschlägen bezieht sich auf inakzeptable Argumente und umfasst vor allem jene, die sich aus Bedingungen ergeben, die theoretisch mit anderen Mitteln behandelt werden können (ohne entstandene Ausnahmen), in einigen Fällen kann dies jedoch nicht als vernünftig angesehen werden Programmierfehler in anderen ein Urteil muss gemacht werden, um die beste Art und Weise, um mit der möglichen Bedingung in nur einer Minderheit behandelt werden, ist eine Ausnahme die einzige glaubwürdige Alternative. Die Ergebnisse sind in Tabelle 1 zusammengefasst. Null-Portname null-Anforderung null-Antwort Wenn portName null ist, wirft der Konstruktor eine Instanz von System. ArgumentNullException (die von System. ArgumentException ableitet) mit dem ParamName-Eigenschaftswert (string) quotPortNamequot. Und die Message-Eigenschaft (Zeichenfolge) Wert quotValue kann nicht null. rnParameter Name: PortNamequot sein. Wenn man den Konstruktor allein betrachtet, ist dies definitiv, da kein anderer Parameter des Konstruktors diese Ausnahme (vorstellbar) auslösen kann. Wenn wir jedoch alle vier Aussagen betrachten, ist es klar, dass eine ArgumentNullException auch aus Read () und Write () kommen kann. Wenn die Anforderung null ist, wirft die Write () - Methode eine Instanz von ArgumentNullException. Mit dem ParamName Wert quotbufferquot und der Message-Wert quotBuffer kann nicht null. rnParameter Name: bufferquot, wenn Antwort null ist, löst die Read () - Methode eine Instanz von ArgumentNullException mit genau den gleichen Eigenschaften. Müssen wir die spezifische Quelle des Problems programmgesteuert identifizieren, die der Try - catch - Blockstruktur von Listing 3 zugesprochen wird, dann wäre es uns nicht möglich, zwischen null - Argumentfehlern von Write () und Read () zu unterscheiden und dies zu tun Zwischen einer dieser Methoden und dem Konstruktor eine Abhängigkeit von dem Wert der ParamName-Eigenschaft erfordern würde. Zum Glück müssen wir uns darum nicht kümmern, denn es ist schwer, sich ein Szenario vorzustellen, in dem wir uns von einem solchen Umstand zurückziehen wollen. Meiner Meinung nach, übergeben einen Nullwert für PortName. Anforderung oder Antwort ist ein Programmierfehler, einfach und einfach, und alle Gedanken der Wiederherstellung sollten vergessen werden. Leerer Portname Wenn portName die leere Zeichenfolge quotquot ist. Der Konstruktor wirft eine Instanz von ArgumentException. Mit dem ParamName Wert quotPortNamequot. Und der Nachrichtenwert quotThe Portname darf nicht leer sein. RnParameter name: PortNamequot. Ungültiger Anschlussname Wenn PortNameC: Windowsquot ist. Der Konstruktor vervollständigt, aber die Open () - Methode wirft eine Instanz von ArgumentException. Mit dem ParamName Wert quotportNamequot. Und der Meldungswert quotDer angegebene Portname startet nicht mit COM / com oder löst keinen gültigen seriellen Port auf. rnParameter name: portNamequot. Null Baudrate Wenn baudRate 0 ist, wirft der Konstruktor eine Instanz von System. ArgumentOutOfRangeException. Mit dem ParamName Wert quotBaudRatequot. Und der Nachrichtenwert quotPositive number required. rNParameter name: BaudRatequot. Negative Baudrate Wenn baudRate -1 ist, ist das Verhalten genau das gleiche wie bei einer Baudrate von Null. Nulldatenbits Wenn dataBits gleich 0 ist, wirft der Konstruktor eine Instanz von ArgumentOutOfRangeException. Mit dem ParamName-Wert quotDataBitsquot und der Message-Wert quotArgument muss zwischen 5 und 8.rnParameter Name: DataBitsquot sein. Negative Datenbits außerhalb des Bereichs befindliche Datenbits Wenn dataBits -1 ist oder irgendeine Zahl außerhalb des (einschließlich) Bereichs 58, ist das Verhalten genau das gleiche wie bei Null-Datenbits. Unbenannte Paritätswerte Wenn Parität (Parität) (- 1) ist. Der Konstruktor wirft eine Instanz von ArgumentOutOfRangeException. Mit dem ParamName-Wert quotParityquot und der Message-Wert quotEnum-Wert außerhalb des legalen Bereichs war. rnParameter name: Parityquot. Es hat das gleiche Verhalten für andere nicht benannte Paritätswerte ENUMS. Unbenannte StopBits-Werte Der Konstruktor weist dasselbe Verhalten wie für nicht benannte Werte von Parity auf (außer dass der Parametername "StopBitsquot" ist). Ungültige Pufferlänge Wenn wir in unserem Aufruf zu Read () die Länge 10 angeben (und genug geschrieben haben, damit es versuchen wird, die nicht vorhandenen Extras 10 zu verwenden), dann werden wir eine Instanz von ArgumentException geworfen. Mit dem ParamName Wert null und der Message-Wert quotOffset und Länge waren außerhalb der Grenzen für das Array oder count ist größer als die Anzahl der Elemente vom Index bis zum Ende der Quelle collection. quot. Im Gegensatz zum Fall mit dem NULL-Portnamen kann argumentiert werden, dass leere und ungültige Portnamen legitime, wenn auch praktisch nicht wiederherstellbare Laufzeitzustände sind: Ein Benutzer kann sich entweder in einem Dialogfeld befinden oder über die Konfigurationsdateieinstellungen (dh Kann falsch sein). Es ist klar, dass die Möglichkeit, einen leeren Namen auf einer höheren Ebene herauszufiltern, einfach und wohl wünschenswert ist, und es hängt von den Details Ihres Designs ab, ob Sie dies tun. Es ist klar, daß Null - und Negativ-Baudraten nicht korrekt sind und daß die Spezifikation entweder als Programmierfehler festgelegt werden könnte (und daher außerhalb des Zuständigkeitsbereichs dieser Klasse, d. h. in der Client-Code-Filterschicht, verhindert werden könnte). Es gibt einige weithin anerkannte Baudraten, aber soweit ich weiß, gibt es keine endgültige endgültige Liste der seriellen Schnittstellen-Baudraten. Daher müssen wir in der Lage sein, einen (positiven) ganzzahligen Wert zu liefern, und wir müssen in der Lage sein, sowohl für das Lesen dieser von irgendwo (zB Konfigurationsbefehlszeile) zur Laufzeit zu berichten, als auch für die Tatsache, dass der dargestellte Wert sein kann Vom Gerät zurückgewiesen wird (z. B. außerhalb seines minimalen / maximalen Bereichs). In den meisten Punkten können Datenbits in gleicher Weise wie die Baudrate betrachtet werden, nur daß der mögliche Bereich (nach Konvention) klein und endlich ist, d. H. Zwischen fünf und acht. Wo die Dinge interessanter sind in den beiden Enumerationstyp-Parameter, Parität und StopBits. Durch die Bereitstellung einer Aufzählung impliziert die API, dass die Menge der Optionen klein, bekannt ist erschöpfend und fest ist, und dass es keine Möglichkeit geben sollte, programmgesteuert einen ungültigen Wert anzugeben. Darüber hinaus, da. NET hat vernünftig gute (nicht großartigen) Einrichtungen für die Interkonvertierung zwischen Enumeration Werte und Zeichenfolgen, sollten wir in der Lage sein (auf der Ebene der Abstraktion von SerialPort) beim Empfang nur gültige Werte. Es gibt drei wichtige Punkte: Der Portname wird nicht validiert (vollständig), bis der Port geöffnet ist. Ich schlage vor, dass dies sinnvoll ist, wenn auch nicht unbedingt wünschenswert Der Wert der ParamName-Eigenschaft quotportNamequot. Unterscheidet sich von dem in den ersten beiden Fällen, wo es "PortNamequot" genannt wurde. Offenbar gibt es eine Inkonsistenz in der Implementierung, und ich schlage vor, dass dies bedeutet, dass wir nicht auf die in ParamName enthaltenen Werte als endgültig (was ich bezweifle, dass jemand darüber nachdenken würde). Ich schlage weiter vor, dass wir alle Framework-Exception-String-Eigenschaften als genaue, präzise und zuverlässige Werte mißtrauen. Obwohl es leicht vorstellbar ist, wie es gekommen ist, so umgesetzt zu werden, ist es dennoch unvorstellbar für mich, dass eine parameterlose Methode Open () In diesem Fall kann eine Instanz der ArgumentException (oder eines seiner abgeleiteten Typen) zu werfen. Vielleicht Im kostbar, aber ich finde dieses enorm vertrauensvolle, wenn mit der Verwendung dieser Komponente konfrontiert. Runtime Ausfälle Diese Klasse von Ausfällen umfasst diejenigen, die vollständig entstehen durch Umstände in der Laufzeitumgebung, und könnte auch von den besten entworfen Programm (was auch immer das bedeutet) erlebt werden. Die Ergebnisse sind in Tabelle 2 zusammengefasst. Unbekannter (aber gültiger) Portname Wenn portName quotCOM9quot ist. Die nicht auf meinem System vorhanden ist, der Konstruktor abgeschlossen, aber die Open () - Methode wirft eine Instanz von System. IO. IOException. Und die Message (string) - Eigenschaft hat den Wert quotDie Port COM9 existiert nicht. quot die InnerException - Eigenschaft ist null. Der HRESULT-Wert, der der Ausnahme zugeordnet ist, ist 0x80131920, das ist COREIO, wie in MSDN für System. IO. IOException dokumentiert. Beachten Sie, dass dies nicht über ein öffentliches Feld / Eigenschaft / Methode verfügbar ist und erfordert Reflexion zu erziehen (wenn möglich). Gerät, das vor dem Schreiben getrennt ist Falls portName gültig ist und einem vorhandenen und verfügbaren Port auf dem aktuellen System entspricht, kann der Aufruf von Open () zurückgegeben werden (was den Erfolg anzeigt). Wenn die Anschlußvorrichtung nachfolgend nicht verfügbar ist, z. B. Durch Depowering oder Trennen des Geräts vom Computer ein Aufruf von Write () führt zum Auslösen einer Instanz von System. IO. IOException. Mit dem Message-Wert quotDas Gerät erkennt den Befehl nicht. Die Eigenschaft InnerException ist null. Der HRESULT-Wert, der mit der Ausnahme assoziiert ist, ist 0x80070016, die keine bekannte Konstante (von der Im bewusst) in seinem eigenen Recht ist. Da jedoch das bekannte FACILITYWIN32 (7) verwendet wird, sollten die unteren 16 Bits einem Windows-Fehlercode entsprechen (definiert in ltFilenamegtWinError. hlt / Filenamegt). Die Windows-Konstante ERRORBADCOMMAND (22L 0x16) ist mit der Meldung quot verbunden. Das Gerät erkennt den Befehl nicht. So dass scheint wie unser Täter. Natürlich enthalten einige. NET-Ausnahmen Windows-Fehlercodes (eingehüllt in HRESULT-Werte). Gerät wurde vor dem Lesen getrennt Wenn der Write () erfolgreich ist, aber das Gerät dann nicht mehr verfügbar ist, führt der nachfolgende Aufruf von Read () zum Auslösen einer Instanz von System. InvalidOperationException. Mit dem Message-Wert quotThe Port ist closed. quot die InnerException-Eigenschaft ist null. Der HRESULT-Wert, der der Ausnahme zugeordnet ist, ist 0x80131509, was COREINVALIDOPERATION ist, wie in MSDN für System. InvalidOperationException dokumentiert. Beachten Sie, dass, genau wie mit IOException. Ist dies nicht über ein öffentliches Feld / Eigenschaft / Methode möglich und erfordert eine Reflexion (wenn möglich). Darüber hinaus kann in einigen Fällen (zB beim Durchführen von Lesevorgängen auf einem anderen Thread über SerialPort s DataReceived-Ereignis, das einen ersten Biss am Gerätausfall haben kann, was zu einer Zustandsänderung führt, so dass) ein Aufruf von Write () ebenfalls zu a führen kann InvalidOperationException ausgelöst. Anstelle von IOException. Es gibt klare Probleme, die von diesen Laufzeitfehlern für das Schiedsverfahren erfolgreich zwischen erstattungsfähigen und praktisch nicht wiederherstellbaren Bedingungen und für die Bereitstellung von nützlichen Informationen in jedem diagnostischen Protokoll Aussagen / Kontingent Berichte in jedem Fall. Vielleicht ist das am wenigsten wichtige Problem, dass zwei der drei Meldungen Grenze an den nutzlosen: "Der Port ist closedquot ist nicht wahrscheinlich, um die normalen oder Power User viel über die selbstverständliche Einsicht, dass das System nicht funktioniert für mich quotLicht das Gerät nicht erleuchten Erkennt das commandquot klingt wie die Basis von etwas Nützlichem, aber es vernachlässigt zu spezifizieren (oder sogar Hinweis auf), welche Befehl: bedeutet es die. NET-Klasse Methode gerade aufgerufen, oder die zugrunde liegende Systemgerät Befehl. Was auch immer, es scheint bizarr, da das Gerät zu einem anderen Zeitpunkt den Befehl zu erkennen, so ist nicht die wahre Botschaft, dass das Gerät kann nicht erfüllen den Befehl ltCOMMANDgt im aktuellen Zustand, oder einige solche nur die Nachricht quotDie Port COM9 nicht existquot ist ausreichend, Insofern es etwas wirklich Bedeutungsvolles liefert, wer den Diagnoseprotokoll / Kontingentbericht lesen wird. Alle verbleibenden Probleme sind ernster und reflektieren, was ich erlebt habe, um ein sehr schlechter Standard des Designs in der. NET Ausnahmehierarchie und der Anwendung von zu sein Die Ausnahmearten in anderen Komponenten, insbesondere solche, die mit dem Betriebssystem interagieren. Ich merke auch hier, dass die Exception. Message-Eigenschaftsdokumentation unklar ist, insbesondere was die Lokalisierung anbelangt, was bedeutet, dass wir uns nicht auf den Nachrichteninhalt mit irgendeiner Präzision verlassen können, wie wir es brauchen würden, wenn wir etwa an die Analyse von Informationen in Ausnahmemeldungen denken würden Um Rückforderungsentscheidungen zu treffen. Die darin enthaltenen Informationen können nur dann als potenziell hilfreich in einem Diagnoseprotokollauszug / Kontingentenbericht herangezogen werden. Erstens, obwohl Ive etwas beiläufig die HRESULT Werte erwähnt, die mit den Ausnahmen verbunden sind, sind diese nicht öffentlich zugänglich. Sie werden in einem privaten Bereich HResult innerhalb des Exception-Typs gespeichert, der für abgeleitete Typen über die HResult protected-Eigenschaft gelesen / schreibend zugänglich ist. Einige Ausnahmeklassen wie IOException. Bieten die Mittel an, einen HRESULT-Wert im Konstruktor zu liefern, der sich direkt auf die Bedingung bezieht, die die Exception ausgelöst hat, wenn nicht vorgesehen, ein Bestandscode, der das Subsystem oder die Exception-Klasse darstellt (zB COREIO für IOException), wie es immer der Fall ist Ausnahmetypen, die keinen solchen Konstruktor bereitstellen (z. B. COREINVALIDOPERATION für InvalidOperationException). Die einzige Möglichkeit, auf diesen Wert zuzugreifen, besteht darin, die Reflektion direkt (z. B. mit Object. GetType (), Type. GetField () FieldInfo. GetValue ()) oder über System. Runtime. InteropServices. Marshal. GetHRForException () zu verwenden. In beiden Fällen wird dies nur in Ausführungskontexten gelingen, in denen der aufrufende Thread die erforderlichen Rechte besitzt. Abgesehen davon, können wir nicht davon ausgehen, gut in der Lage, den Wert zugreifen, die ziemlich viel tötet es für Allzweck-Programmierung. Und es wird noch schlimmer. Die dem HRESULT-Wert zugeschriebene Bedeutung ist nicht konsistent. In den obigen drei Fällen ist es nur im Device Disconnected Before Write-Fall, der von einer Instanz von IOException berichtet wird, bedingungsspezifisch. In den beiden anderen Fällen ist es sub-system / exception-class-spezifisch, von einem von InvalidOperationException und das andere von IOException berichtet. Wir können keine bedingungsspezifischen Informationen sogar innerhalb eines (IOException - rooted) Zweigs des Exception-Stammbaums erwarten. Drittens, da (i).NET keine Ausnahmen überprüft hat und die Dokumentation immer mit einem Körnchen Salz aufgenommen werden muss, und (ii) Ive das oben beschriebene Verhalten durch Tests, die notwendigerweise unerschöpflich sein werden, entdeckt haben, müssen wir die offensichtliche Frage stellen : How do we know there arent others Indeed, the documentation states that: Open() may also throw System. UnauthorizedAccessException Read() may also throw System. TimeoutException Write() may also throw System. ServiceProcess. TimeoutException. Note the two different TimeoutException types. If the documentation is correct, its bad design, which does not engender confidence. If the documentation is wrong, its bad documentation, which does not engender confidence. Imagine now, if you will, how we might actually use the serial port in the wild. In one of the daemons that were developing we are interfacing to an external hardware device via a serial port. The device runs continually, and the daemon must (attempt to) maintain connectivity to it on an ongoing basis. In such a case it is essential to be able to react differently to a practically-unrecoverable condition, such as the wrong port name being specified in the daemon configuration information ( Unknown Port Name ), and a (potentially) recoverable loss of connectivity to the device ( Device Disconnected. ) . In the former case, we want to react to the reported condition by issuing diagnostic and contingent report information and terminating the process (since theres absolutely no sense in continuing) in the latter we want the daemon to issue diagnostic information (which will raise alarms in the wider system environment) but retry continually (until it reconnects or until a human tells it to stop). In order to write that system to these requirements, we need to be able to distinguish between the failures. Thus, the final and most compelling, disturbing, and disabling problem is that the. NET SerialPort component does not support our eminently sensible failure behaviour requirements, because the Unknown Port Name condition and the Device Disconnected. conditions are reported by the same type of exception, IOException. whose messages we must not parse (since we do not know if we can trust them), and whose putatively definitive discriminating HRESULT information is assigned inconsistently and may not even be accessible There is no way, with the given type and state information provided, to discriminate between these two conditions. Contrast this with the situation in C, programming with the Windows API functions CreateFile(). WriteFile(). and ReadFile(). if we pass an unknown COM port we get ERRORFILENOTFOUND if we pass quotC:Windowsquot we get ERRORACCESSDENIED if we have a comms failure we get ERRORBADCOMMAND. Its certainly arguable that the latter two constants names do not directly bear on the conditions that precipitated them, but the point is that programming at the C-level allows us to discriminate these conditions by state, relying on accessible and (as far as I can tell) predictable and reliable values programming at the C-level (using the. NET standard library) does not. Resort to context This only leaves context. Our only recourse is to wrap separately the calls to Open() and Write() in try - catch in order to intercept the useless-in-a-wider-context exceptions and translate them into something definitive, along the lines shown in Listing 4. Lets be clear about this: what were trying to do with the serial port is a combination of good practices simplicity, abstraction, transparency in so far as were focusing on the normative code, and relying on the sophistication of exception-handling to allow us to deal with failures elsewhere. Unfortunately, the. NET exception hierarchy is poorly designed and badly applied, so weve been forced to pollute the code with low-level try - catch handlers, using context to rebuild the missing type / state information before passing on the reported failure condition its arguable that using P/Invoke to get at the Windows API calls and using return codes would have been better, which is quite a reversal Weve had to violate one of the important purposes/advantages of the exception-paradigm, the separation of normative code from failure-handling code, for the purposes of increased transparency and expressiveness. In this case, making the code adequately robust results in a serious detraction from both. Thankfully, we can rely on old reliable another level of indirection by wrapping all the filth in a class, although we cannot hide from the burden of creating appropriate exception types, nor hide our client code completely from the burden of understanding and coupling to them. All the details of which I now cunningly leave until next time. And now for something slightly different. Quench. NET Quench is an open-source library designed to: Facilitate diagnosis and correction of badly-written application code that inappropriately quenches exceptions and Provides assistance in the use (and correction of that use) of badly-designed standard and third-party components that indicate failure, via thrown exceptions, without providing adequate information to delineate unambiguously between practically-unrecoverable and recoverable conditions In both regards, Quench facilitates post-hoc discovery and adjustment of application-behaviour, while ensuring that safe defaults are applied, at varying levels of precision. Quench. NET is the first (and currently only) application of the Quench design principle others will follow in due course. There be dragons Consider the (C) code fragments in Listings 58.I find it deeply concerning to see such code in production software. Over the last couple of years Ive had occasion to work with codebases containing literally thousands of inappropriate exception quenches indeed, the extent of such constructs in one codebase meant that a case-by-case remediation was quite impractical. (I find it even more disheartening to see code such as this in strongly-selling and widely-recommended text books Ive encountered the second in one such book I read last year. There is a considerable challenge in writing worthwhile material about programming because publishers and readers, according to publishers want only pithy tomes that can fit in a pocket and be read in a day. As a consequence, many books show simplistic views of real programs (and program fragments) that may not reflect fairly their authors practice in order to focus on their subject and to present digestible quanta of material to the reader. As I have already acknowledged in QM5 , this is a hard conflict to redress satisfactorily: certainly, I am not sure that I have not erred previously in this way myself. Nonetheless, now having realised the full import and complexity of failure-handling, I cant forget it, and I cant forgive books and articles that mislead, since I meet far too many programmers who have been misled.) In the first case (Listing 5), the user intends to try an operation that may fail (and will indicate its failure by throwing an exception), and to provide some reasonable default instead if it does so. This is a perfectly reasonable intent, just realised badly. The problem is, catching Exception (rather than the appropriate precise expected exception type) is far too broad: every exception in the. NET ecology derives from Exception. and this code will quench (almost) all of them, including those that are practically-unrecoverable (such as System. OutOfMemoryException ). Dragon The way to do this properly is as shown in Listing 9: its hardly any more effort to catch only the exception representing the failure we want to intercept, rather than (almost) all possible failures.(Note: prior to. NET 2 this Parse()ampcatch was the way to try-to-parse a number (or date, or IP address, or. ) from a string. Thankfully, this ugly and inefficient technique was obviated with the introduction of the TryParse() methods, which return true or false. and do not need to throw System. FormatException .) The second case (Listing 6) also represents good intentions, and has a veneer of robustness insofar as it issues a diagnostic log statement. Alas, this is specious comfort. First, diagnostic log statements are subject to the principle of removability QM6 , so provision of a diagnostic log statement alone is an invitation to do nothing. Rather, the (removable) diagnostic log statement should be associated with additional (non-removable) action, whether that be to set a flag, throw a different exception, return, or whatever. Second, we still have the huge but subtle issue that were catching everything . including those things that should denote practically-unrecoverable conditions. Furthermore, the text book to which Ive alluded above has a construct like this albeit that its notionally a contingent report, in the form of Console. WriteLine(x) at the outermost scope in Main(). so it fails the requirement to indicate failure to the operating environment QM6 : any exception is caught and yet the program returns 0, indicating successful execution, to the operating environment. Dragon The third case is not even defensible from the position of good intentions gone bad. This is flat-out, unequivocal, inexcusable, malpractice. If you write code such as this, you dont deserve to earn a crust as a programmer. If you encounter code such as this and dont raise the alarm to your team lead, manager, head of IT. then youre being incredibly reckless and risking having to deal with system failures that you may be literally clueless to diagnose. The problem is a distilled version of the slip weve seen in the first two cases: when you write code such as this you are asserting I know everything that can possibly happen and I deem it all to be of no consequence. Apart from the most trivial cases, I doubt anyone can stand behind either half of that assertion. Dragon The fourth case (Listing 8) is the same as the third (Listing 7) just a bit of syntactic sugar() provided by the C language, to avoid the unused reference warning that would result from compiling the code from Listing 7 at warning level 3 or 4. Here, the language designers have gone out of their way to ease the application of a total anti-pattern. Honestly (More a case of Bats in the Belfry than Dragon )

No comments:

Post a Comment