Ich habe vor kurzem einen C # -Programmierjob begonnen, habe aber einiges an Hintergrundwissen in Haskell.
Aber ich verstehe, dass C # eine objektorientierte Sprache ist, ich möchte keinen runden Stift in ein quadratisches Loch zwingen.
Ich habe den Artikel Exception Throwing von Microsoft gelesen, in dem es heißt:
KEINE Fehlercodes zurückgeben.
Aber da ich an Haskell gewöhnt bin, habe ich den C # -Datentyp verwendet OneOf
und das Ergebnis als "richtigen" Wert oder den Fehler (meistens eine Aufzählung) als "linken" Wert zurückgegeben.
Dies ähnelt der Konvention von Either
in Haskell.
Das erscheint mir sicherer als Ausnahmen. In C # führt das Ignorieren von Ausnahmen nicht zu einem Kompilierungsfehler. Wenn sie nicht abgefangen werden, sprudeln sie nur und stürzen Ihr Programm ab. Dies ist vielleicht besser, als einen Fehlercode zu ignorieren und undefiniertes Verhalten zu erzeugen, aber das Abstürzen der Client-Software ist immer noch keine gute Sache, insbesondere, wenn viele andere wichtige Geschäftsaufgaben im Hintergrund ausgeführt werden.
Bei OneOf
muss man ziemlich explizit sein, wie man es auspackt und mit dem Rückgabewert und den Fehlercodes umgeht. Und wenn man zu diesem Zeitpunkt im Aufrufstapel nicht weiß, wie man damit umgeht, muss es in den Rückgabewert der aktuellen Funktion eingefügt werden, damit die Aufrufer wissen, dass ein Fehler auftreten kann.
Dies scheint jedoch nicht der Ansatz zu sein, den Microsoft vorschlägt.
Ist die Verwendung von OneOf
anstelle von Ausnahmen zum Behandeln von "normalen" Ausnahmen (wie "Datei nicht gefunden" usw.) ein vernünftiger Ansatz oder ist es eine schreckliche Praxis?
Es ist erwähnenswert, dass ich gehört habe, dass Ausnahmen als Kontrollfluss ein ernstes Antipattern darstellen . Wenn die "Ausnahme" also etwas ist, mit dem Sie normalerweise umgehen würden, ohne das Programm zu beenden, ist das nicht eine Art "Kontrollfluss"? Soweit ich weiß, gibt es hier eine Grauzone.
Hinweis Ich verwende nicht OneOf
für Dinge wie "Out of Memory". Bedingungen, von denen ich keine Wiederherstellung erwarte, lösen weiterhin Ausnahmen aus. Ich halte es jedoch für vernünftig, wenn Benutzereingaben, die nicht analysiert werden, im Wesentlichen "Kontrollfluss" sind und wahrscheinlich keine Ausnahmen auslösen sollten.
Nachfolgende Gedanken:
Aus dieser Diskussion nehme ich derzeit Folgendes mit:
- Wenn Sie erwarten, dass der unmittelbare Aufrufer
catch
die Ausnahme die meiste Zeit behandelt und seine Arbeit fortsetzt, möglicherweise über einen anderen Pfad, sollte er wahrscheinlich Teil des Rückgabetyps sein.Optional
oderOneOf
kann hier nützlich sein. - Wenn Sie erwarten, dass der unmittelbare Aufrufer die Ausnahme die meiste Zeit nicht abfängt, lösen Sie eine Ausnahme aus, um die Dummheit zu vermeiden, sie manuell über den Stapel weiterzuleiten.
- Wenn Sie nicht sicher sind, was der unmittelbare Anrufer tun wird, geben Sie möglicherweise beides an, wie
Parse
undTryParse
.
quelle
Result
;-)FileNotFoundException
. H.Either
Monade kein Fehlercode ist und auch nichtOneOf
? Sie unterscheiden sich grundlegend, und die Frage scheint folglich auf einem Missverständnis zu beruhen. (Obwohl in modifizierter Form, ist es immer noch eine gültige Frage.)Antworten:
Es ist mit Sicherheit eine gute Sache.
Sie möchten, dass alles, was das System in einem undefinierten Zustand belässt, das System stoppt, da ein undefiniertes System böse Dinge wie beschädigte Daten ausführen, die Festplatte formatieren und E-Mails an den Präsidenten senden kann. Wenn Sie das System nicht wiederherstellen und in einen definierten Zustand zurückversetzen können, ist ein Absturz die verantwortliche Maßnahme. Genau deshalb bauen wir Systeme, die abstürzen, anstatt sich leise auseinander zu reißen. Nun sicher, wir alle wollen ein stabiles System, das niemals abstürzt, aber das wollen wir wirklich nur, wenn das System in einem definierten vorhersagbaren sicheren Zustand bleibt.
Das ist absolut richtig, wird aber oft missverstanden. Als sie das Ausnahmesystem erfanden, befürchteten sie, die strukturierte Programmierung zu brechen. Strukturierte Programmierung Deshalb haben wir
for
,while
,until
,break
, und ,continue
wenn alles , was wir brauchen, all das zu tun, istgoto
.Dijkstra lehrte uns, dass die informelle Verwendung von goto (das heißt herumspringen, wo immer Sie möchten) das Lesen von Code zu einem Albtraum macht. Als sie uns das Ausnahmesystem gaben, hatten sie Angst, dass sie goto neu erfinden würden. Also sagten sie uns, wir sollten es nicht zur Flusskontrolle verwenden, in der Hoffnung, dass wir es verstehen würden. Leider haben viele von uns dies nicht getan.
Seltsamerweise missbrauchen wir nicht oft Ausnahmen, um Spaghetti-Code zu erstellen, wie wir es früher mit goto getan haben. Der Rat selbst scheint mehr Ärger verursacht zu haben.
Grundsätzlich geht es bei Ausnahmen darum, eine Annahme abzulehnen. Wenn Sie nach dem Speichern einer Datei fragen, gehen Sie davon aus, dass die Datei gespeichert werden kann und wird. Die Ausnahme, die Sie bekommen, wenn es nicht möglich ist, kann sein, dass der Name illegal ist, die Festplatte voll ist oder dass eine Ratte durch Ihr Datenkabel genagt hat. Sie können alle diese Fehler unterschiedlich behandeln, Sie können sie alle auf dieselbe Weise behandeln, oder Sie können sie das System anhalten lassen. In Ihrem Code gibt es einen glücklichen Pfad, auf dem Ihre Annahmen zutreffen müssen. Die eine oder andere Ausnahme bringt Sie von diesem glücklichen Weg ab. Genau genommen ist das eine Art "Flusskontrolle", aber davor haben sie dich nicht gewarnt. Sie sprachen über Unsinn wie folgt :
"Ausnahmen sollten außergewöhnlich sein". Diese kleine Tautologie wurde ins Leben gerufen, weil die Entwickler von Ausnahmesystemen Zeit benötigen, um Stack-Traces zu erstellen. Im Vergleich zum Herumspringen ist dies langsam. Es frisst CPU-Zeit. Wenn Sie jedoch das System protokollieren und anhalten oder zumindest die aktuelle zeitintensive Verarbeitung anhalten möchten, bevor Sie die nächste starten, haben Sie etwas Zeit, um sie zu beenden. Wenn Leute anfangen, Ausnahmen "zur Flusskontrolle" zu verwenden, gehen diese Annahmen über die Zeit alle aus dem Fenster. "Ausnahmen sollten außergewöhnlich sein" wurde uns wirklich als Leistungsüberlegung gegeben.
Viel wichtiger als das verwirrt uns nicht. Wie lange haben Sie gebraucht, um die Endlosschleife im obigen Code zu erkennen?
... ist ein guter Rat, wenn Sie sich in einer Codebasis befinden, in der normalerweise keine Fehlercodes verwendet werden. Warum? Denn niemand wird sich daran erinnern, den Rückgabewert zu speichern und Ihre Fehlercodes zu überprüfen. Es ist immer noch eine gute Konvention, wenn Sie in C. sind
Sie verwenden noch eine andere Konvention. Das ist in Ordnung, solange Sie die Konvention festlegen und nicht einfach gegen eine andere kämpfen. Es ist verwirrend, zwei Fehlerkonventionen in derselben Codebasis zu haben. Wenn Sie den gesamten Code, der die andere Konvention verwendet, entfernt haben, fahren Sie fort.
Ich mag die Convention selbst. Eine der besten Erklärungen dafür fand ich hier * :
Aber so sehr es mir gefällt, werde ich es trotzdem nicht mit den anderen Konventionen mischen. Wähle einen aus und bleibe dabei. 1
1: Damit meine ich, dass ich nicht über mehrere Konventionen gleichzeitig nachdenke.
Es ist wirklich nicht so einfach. Eines der grundlegenden Dinge, die Sie verstehen müssen, ist, was eine Null ist.
Wie viele Tage sind noch im Mai? 0 (weil es nicht Mai ist. Es ist schon Juni).
Ausnahmen sind eine Möglichkeit, eine Annahme abzulehnen, aber nicht die einzige. Wenn Sie Ausnahmen verwenden, um die Annahme abzulehnen, verlassen Sie den glücklichen Pfad. Wenn Sie jedoch Werte ausgewählt haben, um den glücklichen Pfad abzusenden, der signalisiert, dass die Dinge nicht so einfach sind, wie angenommen wurde, können Sie auf diesem Pfad bleiben, solange er mit diesen Werten umgehen kann. Manchmal bedeutet 0 bereits etwas, sodass Sie einen anderen Wert finden müssen, um Ihre Annahme, die eine Idee ablehnt, darauf abzubilden. Sie erkennen diese Idee vielleicht an ihrer Verwendung in der guten alten Algebra . Monaden können dabei helfen, aber es muss nicht immer eine Monade sein.
Zum Beispiel 2 :
Kannst du dir einen guten Grund vorstellen, warum dies so gestaltet sein muss, dass es absichtlich irgendetwas auslöst? Rate mal, was du bekommst, wenn kein Int geparst werden kann? Ich muss es dir nicht einmal sagen.
Das ist ein Zeichen für einen guten Namen. Sorry, aber TryParse ist nicht meine Vorstellung von einem guten Namen.
Wir vermeiden es oft, eine Ausnahme zu machen, wenn die Antwort mehr als eine Sache zur gleichen Zeit sein könnte, aber aus irgendeinem Grund, wenn die Antwort entweder eine Sache oder nichts ist, werden wir davon besessen, darauf zu bestehen, dass sie uns eine Sache gibt oder wirft:
Müssen parallele Linien hier wirklich eine Ausnahme verursachen? Ist es wirklich so schlimm, wenn diese Liste niemals mehr als einen Punkt enthält?
Vielleicht kann man das semantisch einfach nicht ertragen. Wenn ja, ist es schade. Aber vielleicht
List
fühlen Sie sich bei Monaden, die keine willkürliche Größe haben , besser.Die Monaden sind kleine Spezialsammlungen, die für bestimmte Zwecke verwendet werden sollen, ohne dass sie getestet werden müssen. Wir sollen Wege finden, mit ihnen umzugehen, unabhängig davon, was sie enthalten. Auf diese Weise bleibt der glückliche Weg einfach. Wenn Sie jede Monade, die Sie berühren, aufschlagen und testen, verwenden Sie sie falsch.
Ich weiß, es ist komisch. Aber für uns ist es ein neues Werkzeug. Also gib ihm etwas Zeit. Hämmer sind sinnvoller, wenn Sie sie nicht mehr für Schrauben verwenden.
Wenn Sie mich verwöhnen, möchte ich diesen Kommentar ansprechen:
Das ist absolut richtig. Monaden sind Sammlungen viel näher als Ausnahmen, Flags oder Fehlercodes. Sie machen feine Behälter für solche Dinge, wenn sie mit Bedacht eingesetzt werden.
quelle
throw
ich eigentlich nicht weiß / nicht sage, wohin wir springen werden;)C # ist nicht Haskell, und Sie sollten dem Experten-Konsens der C # -Community folgen. Wenn Sie stattdessen versuchen, die Haskell-Praktiken in einem C # -Projekt zu befolgen, entfremden Sie alle anderen Mitglieder Ihres Teams und entdecken möglicherweise die Gründe, warum die C # -Community die Dinge anders ausführt. Ein wichtiger Grund ist, dass C # diskriminierte Gewerkschaften nicht bequem unterstützt.
Dies ist keine allgemein akzeptierte Wahrheit. In jeder Sprache, die Ausnahmen unterstützt, können Sie entweder eine Ausnahme auslösen (die der Aufrufer nicht behandeln kann) oder einen zusammengesetzten Wert zurückgeben (den der Aufrufer behandeln MUSS).
Die Weitergabe von Fehlerbedingungen über den Aufrufstapel nach oben erfordert eine Bedingung auf jeder Ebene, die die zyklomatische Komplexität dieser Methoden verdoppelt und folglich die Anzahl der Einzeltestfälle verdoppelt. In typischen Geschäftsanwendungen sind viele Ausnahmen nicht mehr wiederherstellbar und können auf die höchste Ebene übertragen werden (z. B. den Serviceeinstiegspunkt einer Webanwendung).
quelle
Sie sind zu einem interessanten Zeitpunkt zu C # gekommen. Bis vor relativ kurzer Zeit saß die Sprache fest im imperativen Programmierraum. Die Verwendung von Ausnahmen zur Übermittlung von Fehlern war definitiv die Norm. Die Verwendung von Rückgabecodes litt unter mangelnder Sprachunterstützung (z. B. diskriminierte Gewerkschaften und Mustervergleich). Die offizielle Anweisung von Microsoft lautete vernünftigerweise, Rückkehrcodes zu vermeiden und Ausnahmen zu verwenden.
Es hat jedoch immer Ausnahmen in Form von
TryXXX
Methoden gegeben, die einboolean
Erfolgsergebnis zurückgeben und ein zweitesout
Wertergebnis über einen Parameter liefern . Diese sind den "try" -Mustern in der funktionalen Programmierung sehr ähnlich, außer dass das Ergebnis über einen out-Parameter und nicht über einenMaybe<T>
Rückgabewert kommt.Aber die Dinge ändern sich. Funktionale Programmierung wird immer beliebter und Sprachen wie C # reagieren. In C # 7.0 wurden einige grundlegende Mustervergleichsfunktionen für die Sprache eingeführt. C # 8 wird noch viel mehr einführen, einschließlich eines Schalterausdrucks, rekursiver Muster usw. Gleichzeitig hat die Anzahl der "funktionalen Bibliotheken" für C # zugenommen, wie z. B. meine eigene Succinc <T> -Bibliothek , die auch diskriminierte Gewerkschaften unterstützt .
Diese beiden Dinge zusammen bedeuten, dass Code wie der folgende langsam an Popularität gewinnt.
Es ist im Moment noch eine ziemlich kleine Nische, obwohl ich selbst in den letzten zwei Jahren einen deutlichen Wandel von allgemeiner Feindseligkeit unter C # -Entwicklern zu solchem Code zu einem wachsenden Interesse an der Verwendung solcher Techniken bemerkt habe.
Wir sind noch nicht da, wenn wir sagen " KEINE Fehlercodes zurückgeben". ist antiquiert und muss in den Ruhestand. Aber der Marsch die Straße hinunter in Richtung dieses Ziels ist in vollem Gange. Treffen Sie also Ihre Wahl: Bleiben Sie bei den alten Methoden, Ausnahmen mit schwulem Hintergrund zu werfen, wenn Sie es so mögen. oder entdecken Sie eine neue Welt, in der Konventionen aus funktionalen Sprachen in C # immer beliebter werden.
quelle
java.util.Stream
(ein halbgesetzter IEnumerable-alike) und Lambdas jetzt, richtig? :)Ich denke, es ist völlig vernünftig, eine DU zu verwenden, um Fehler zurückzugeben, aber dann würde ich das sagen, als ich OneOf schrieb :) Aber ich würde das trotzdem sagen, da es in F # usw. üblich ist (z. B. der von anderen erwähnte Ergebnistyp) ).
Ich denke nicht an die Fehler, die ich normalerweise als Ausnahmesituationen zurückgebe, sondern als normale, aber hoffentlich selten auftretende Situationen, die möglicherweise behandelt werden müssen oder nicht, aber modelliert werden sollten.
Hier ist das Beispiel von der Projektseite.
Das Auslösen von Ausnahmen für diese Fehler erscheint als Overkill - sie haben einen Performance-Overhead, abgesehen von den Nachteilen, dass sie nicht explizit in der Methodensignatur enthalten sind oder einen umfassenden Abgleich bieten.
Inwieweit OneOf in c # idiomatisch ist, ist eine andere Frage. Die Syntax ist gültig und ziemlich intuitiv. DUs und Rail-orientierte Programmierung sind bekannte Konzepte.
Ich würde Ausnahmen für Dinge speichern, die darauf hinweisen, dass etwas kaputt ist, wo dies möglich ist.
quelle
OneOf
es darum geht, den Rückgabewert reicher zu machen, wie eine Nullable zurückzugeben. Es ersetzt Ausnahmen für Situationen, die anfangs nicht außergewöhnlich sind.OneOf
entspricht in etwa überprüften Ausnahmen in Java. Es gibt einige Unterschiede:OneOf
funktioniert nicht mit Methoden, die Methoden erzeugen, die für ihre Nebenwirkungen aufgerufen werden (da sie möglicherweise sinnvoll aufgerufen werden und das Ergebnis einfach ignoriert wird). Natürlich sollten wir alle versuchen, reine Funktionen zu verwenden, aber das ist nicht immer möglich.OneOf
gibt keine Auskunft darüber, wo das Problem aufgetreten ist. Dies ist gut, da es die Leistung spart (ausgelöste Ausnahmen in Java sind kostspielig, weil der Stack-Trace gefüllt wird, und in C # ist es dasselbe) und Sie dazu zwingt, im FehlermitgliedOneOf
selbst ausreichende Informationen bereitzustellen . Es ist auch schlecht, da diese Informationen möglicherweise unzureichend sind und Sie Schwierigkeiten haben können, die Ursache des Problems zu finden.OneOf
und geprüfte Ausnahmen haben ein wichtiges "Merkmal" gemeinsam:Dies verhindert deine Angst
aber wie gesagt, diese angst ist irrational. Grundsätzlich gibt es normalerweise eine einzige Stelle in Ihrem Programm, an der Sie alles erfassen müssen (wahrscheinlich verwenden Sie dafür bereits ein Framework). Da Sie und Ihr Team normalerweise Wochen oder Jahre an dem Programm arbeiten, werden Sie es nicht vergessen, oder?
Der große Vorteil ignorierbarer Ausnahmen besteht darin, dass sie überall dort behandelt werden können, wo Sie sie behandeln möchten, und nicht überall im Stack-Trace. Dies eliminiert Tonnen von Boilerplate (sehen Sie sich nur einige Java-Codes an, die "Throws" deklarieren oder Ausnahmen einschließen) und macht Ihren Code weniger fehleranfällig: Da jede Methode Throws verursachen kann, müssen Sie sich dessen bewusst sein. Glücklicherweise bewirkt die richtige Aktion normalerweise nichts , dh, dass sie an einen Ort sprudelt, an dem sie vernünftig gehandhabt werden kann.
quelle
OneOf<SomeSuccess, SomeErrorInfo>
wo dieSomeErrorInfo
Einzelheiten des Fehlers zur Verfügung stellt.Fehler haben eine Reihe von Dimensionen. Ich identifiziere drei Dimensionen: Können sie verhindert werden, wie oft sie auftreten und ob sie wiederhergestellt werden können? Unterdessen hat die Fehlersignalisierung hauptsächlich eine Dimension: zu entscheiden, inwieweit der Anrufer über den Kopf geschlagen werden soll, um ihn zur Behebung des Fehlers zu zwingen, oder den Fehler ausnahmsweise "leise" in die Luft sprudeln zu lassen.
Die nicht vermeidbaren, häufigen und behebbaren Fehler müssen vor Ort behoben werden. Sie rechtfertigen es am besten, den Anrufer zu zwingen, sich ihnen zu stellen. In Java mache ich sie zu geprüften Ausnahmen, und ein ähnlicher Effekt wird erzielt, wenn man sie macht
Try*
Methoden macht oder eine diskriminierte Union zurückgibt. Diskriminierte Gewerkschaften sind besonders nett, wenn eine Funktion einen Rückgabewert hat. Sie sind eine viel raffiniertere Version der Rückkehrnull
. Die Syntax vontry/catch
Blöcken ist nicht besonders gut (zu viele Klammern), sodass Alternativen besser aussehen. Ausnahmen sind außerdem etwas langsam, da sie Stack-Traces aufzeichnen.Die vermeidbaren Fehler (die aufgrund von Programmierfehlern / Nachlässigkeiten nicht verhindert wurden) und die nicht behebbaren Fehler funktionieren wie gewöhnliche Ausnahmen. Seltene Fehler funktionieren auch besser als gewöhnliche Ausnahmen, da ein Programmierer es oftmals als nicht vorhersehbar ansieht (dies hängt vom Zweck des Programms ab).
Ein wichtiger Punkt ist, dass es häufig die Verwendungsstelle ist, die bestimmt, wie die Fehlermodi einer Methode in diese Dimensionen passen. Dies sollte beachtet werden, wenn Folgendes berücksichtigt wird.
Nach meiner Erfahrung ist es in Ordnung, den Anrufer zu zwingen, Fehlerzuständen zu begegnen, wenn Fehler nicht vermeidbar, häufig und behebbar sind, aber sehr ärgerlich, wenn der Anrufer gezwungen ist, durch Rahmen zu springen, wenn er sie nicht benötigt oder möchte . Dies kann daran liegen, dass der Aufrufer weiß, dass der Fehler nicht auftritt, oder dass er den Code (noch) nicht ausfallsicher machen möchte. In Java erklärt dies die Angst vor der häufigen Verwendung von geprüften Ausnahmen und der Rückgabe von Optional (ähnlich wie "Vielleicht" und wurde kürzlich in vielen Kontroversen eingeführt). Wenn Sie Zweifel haben, werfen Sie Ausnahmen und lassen Sie den Anrufer entscheiden, wie er mit ihnen umgehen möchte.
Denken Sie zum Schluss daran, dass das Wichtigste bei Fehlerzuständen ist, sie gründlich zu dokumentieren , und zwar weit über jede andere Überlegung, wie sie beispielsweise signalisiert werden .
quelle
Die anderen Antworten haben Ausnahmen und Fehlercodes ausführlich genug besprochen, daher möchte ich der Frage eine weitere spezifische Perspektive hinzufügen:
OneOf ist kein Fehlercode, sondern eher eine Monade
Bei der Verwendung handelt
OneOf<Value, Error1, Error2>
es sich um einen Container, der entweder das tatsächliche Ergebnis oder einen Fehlerstatus darstellt. Es ist wie einOptional
, außer dass, wenn kein Wert vorhanden ist, es mehr Details liefern kann, warum das so ist.Das Hauptproblem bei Fehlercodes besteht darin, dass Sie vergessen, sie zu überprüfen. Aber hier können Sie buchstäblich nicht auf das Ergebnis zugreifen, ohne nach Fehlern zu suchen.
Das einzige Problem ist, wenn Sie sich nicht um das Ergebnis kümmern. Das Beispiel aus der OneOf-Dokumentation enthält:
... und dann erstellen sie unterschiedliche HTTP-Antworten für jedes Ergebnis, was viel besser ist als die Verwendung von Ausnahmen. Aber wenn Sie die Methode so aufrufen.
dann hast du ein problem und ausnahmen wären die bessere wahl. Vergleichen Rail
save
vssave!
.quelle
Eine Sache, die Sie berücksichtigen müssen, ist, dass Code in C # im Allgemeinen nicht rein ist. In einer Codebasis voller Nebenwirkungen und mit einem Compiler, der sich nicht darum kümmert, was Sie mit einem Rückgabewert einer Funktion tun, kann Ihr Ansatz zu erheblichem Kummer führen. Wenn Sie beispielsweise über eine Methode verfügen, mit der eine Datei gelöscht wird, möchten Sie sicherstellen, dass die Anwendung einen Fehler bei der Ausführung der Methode feststellt, obwohl es keinen Grund gibt, den Fehlercode "auf dem fehlerfreien Pfad" zu überprüfen. Dies ist ein erheblicher Unterschied zu funktionalen Sprachen, bei denen Rückgabewerte, die Sie nicht verwenden, explizit ignoriert werden müssen. In C # werden Rückgabewerte immer implizit ignoriert (die einzige Ausnahme sind Property Getters IIRC).
Der Grund, warum MSDN Sie auffordert, keine Fehlercodes zurückzugeben, ist genau das. Niemand zwingt Sie, den Fehlercode überhaupt zu lesen . Der Compiler hilft Ihnen überhaupt nicht. Allerdings , wenn Ihre Funktion Nebeneffekt frei ist, Sie können sicher etwas verwenden , wie
Either
- der Punkt ist , dass selbst wenn Sie das Fehlerergebnis (falls vorhanden) zu ignorieren, können Sie nur das tun , wenn Sie das nicht „richtige Ergebnis“ verwenden entweder. Es gibt einige Möglichkeiten, wie Sie dies erreichen können. Beispielsweise können Sie das "Lesen" des Werts möglicherweise nur zulassen, indem Sie einen Delegierten für die Behandlung der Erfolgs - und Fehlerfälle übergeben, und Sie können dies problemlos mit Ansätzen wie der eisenbahnorientierten Programmierung kombinieren (dies ist der Fall) funktioniert gut in C #).int.TryParse
ist irgendwo in der Mitte. Es ist rein. Hiermit wird festgelegt, wie die Werte der beiden Ergebnisse zu jedem Zeitpunkt lauten. Sie wissen also, dassfalse
der Ausgabeparameter bei einem Rückgabewert auf gesetzt wird0
. Es hindert Sie immer noch nicht daran, den Ausgabeparameter zu verwenden, ohne den Rückgabewert zu überprüfen, aber es ist zumindest gewährleistet, dass das Ergebnis auch dann angezeigt wird, wenn die Funktion fehlschlägt.Aber eine absolut entscheidende Sache ist hier die Konsistenz . Der Compiler wird Sie nicht retten, wenn jemand diese Funktion ändert, um Nebenwirkungen zu haben. Oder um Ausnahmen zu werfen. Während dieser Ansatz in C # völlig in Ordnung ist (und ich verwende ihn), müssen Sie sicherstellen, dass jeder in Ihrem Team ihn versteht und anwendet . Viele der Invarianten, die für eine funktionierende Programmierung erforderlich sind, werden vom C # -Compiler nicht erzwungen, und Sie müssen sicherstellen, dass sie selbst befolgt werden. Sie müssen sich über die Probleme im Klaren sein, die auftreten können, wenn Sie die von Haskell standardmäßig festgelegten Regeln nicht einhalten. Dies sollten Sie bei allen Funktionsparadigmen berücksichtigen, die Sie in Ihren Code einführen.
quelle
Die richtige Anweisung wäre: Verwenden Sie keine Fehlercodes für Ausnahmen! Und verwenden Sie keine Ausnahmen für Nicht-Ausnahmen.
Wenn etwas passiert, von dem Ihr Code nicht wiederhergestellt werden kann (dh funktioniert), ist dies eine Ausnahme. Es gibt nichts, was Ihr sofortiger Code mit einem Fehlercode tun würde, außer ihn weiterzugeben, um ihn zu protokollieren oder dem Benutzer möglicherweise auf irgendeine Weise bereitzustellen. Genau dafür gibt es jedoch Ausnahmen - sie kümmern sich um das ganze Herumreichen und helfen bei der Analyse, was tatsächlich passiert ist.
Wenn jedoch etwas passiert, wie eine ungültige Eingabe, die Sie verarbeiten können, indem Sie sie entweder automatisch neu formatieren oder den Benutzer auffordern, die Daten zu korrigieren (und Sie haben an diesen Fall gedacht), ist dies - wie Sie - keine wirkliche Ausnahme erwarte es. Sie können diese Fälle jederzeit mit Fehlercodes oder einer anderen Architektur behandeln. Nur keine Ausnahmen.
(Beachten Sie, dass ich in C # nicht sehr erfahren bin, aber meine Antwort sollte im Allgemeinen auf der konzeptionellen Ebene der Ausnahmen basieren.)
quelle
catch
Schlüsselwort nicht vorhanden. Und da wir in einem C # -Kontext sprechen, ist es erwähnenswert, dass die hier vertretene Philosophie nicht vom .NET-Framework befolgt wird. Vielleicht ist das einfachste vorstellbare Beispiel für "ungültige Eingaben, die Sie verarbeiten können", dass der Benutzer eine Nicht-Nummer in ein Feld eingibt, in dem eine Nummer erwartet wird ... undint.Parse
eine Ausnahme auslöst.Es ist eine "schreckliche Idee".
Es ist schrecklich, denn zumindest in .net gibt es keine vollständige Liste möglicher Ausnahmen. Jeder Code kann möglicherweise eine Ausnahme auslösen. Insbesondere, wenn Sie OOP ausführen und überschriebene Methoden für einen Untertyp anstelle des deklarierten Typs aufrufen könnten
Sie müssten also alle Methoden und Funktionen auf ändern.
OneOf<Exception, ReturnType>
Wenn Sie dann verschiedene Ausnahmetypen behandeln möchten, müssen Sie den TypIf(exception is FileNotFoundException)
usw. Untersuchentry catch
ist das Äquivalent vonOneOf<Exception, ReturnType>
Bearbeiten ----
Mir ist bewusst, dass Sie eine Rückkehr vorschlagen,
OneOf<ErrorEnum, ReturnType>
aber ich bin der Meinung, dass dies ein Unterschied ohne Unterschied ist.Sie kombinieren Rückkehrcodes, erwartete Ausnahmen und Verzweigen nach Ausnahme. Der Gesamteffekt ist jedoch der gleiche.
quelle
Exception
s zurückzugeben.