Ich bin ein professioneller C-Programmierer und ein Hobby-Obj-C-Programmierer (OS X). Vor kurzem war ich aufgrund seiner sehr umfangreichen Syntax versucht, in C ++ zu expandieren.
Bisher habe ich mich nicht viel mit Ausnahmen befasst. Objective-C hat sie, aber Apples Richtlinien sind ziemlich streng:
Wichtig Sie sollten die Verwendung von Ausnahmen für Programmier- oder unerwartete Laufzeitfehler reservieren, z. B. Zugriff auf die Sammlung außerhalb der Grenzen, Versuche, unveränderliche Objekte zu mutieren, eine ungültige Nachricht zu senden und die Verbindung zum Fensterserver zu verlieren.
C ++ scheint es vorzuziehen, Ausnahmen häufiger zu verwenden. Zum Beispiel löst das Wikipedia-Beispiel auf RAII eine Ausnahme aus, wenn eine Datei nicht geöffnet werden kann. Objective-C würde return nil
mit einem Fehler von einem out-Parameter gesendet. Insbesondere scheint std :: ofstream so oder so eingestellt werden zu können.
Hier bei Programmierern habe ich mehrere Antworten gefunden, die entweder proklamieren, Ausnahmen anstelle von Fehlercodes zu verwenden oder überhaupt keine Ausnahmen zu verwenden . Ersteres scheint häufiger zu sein.
Ich habe niemanden gefunden, der eine objektive Studie für C ++ durchführt. Da Zeiger selten sind, muss ich interne Fehlerflags verwenden, wenn ich Ausnahmen vermeiden möchte. Wird es zu viel Mühe geben, damit umzugehen, oder wird es vielleicht sogar besser funktionieren als Ausnahmen? Ein Vergleich beider Fälle wäre die beste Antwort.
Edit: Obwohl nicht ganz relevant, sollte ich wahrscheinlich klären, was nil
ist. Technisch ist es das gleiche wie NULL
, aber die Sache ist, es ist in Ordnung, eine Nachricht an zu senden nil
. Sie können also so etwas tun
NSError *err = nil;
id obj = [NSFileHandle fileHandleForReadingFromURL:myurl error:&err];
[obj retain];
auch wenn der erste Anruf zurückkam nil
. Und wie Sie es *obj
in Obj-C nie tun , besteht kein Risiko einer NULL-Zeiger-Dereferenzierung.
quelle
Antworten:
Ich würde in mancher Hinsicht tatsächlich weniger als Objective-C vorschlagen, da die C ++ - Standardbibliothek im Allgemeinen keine Programmiererfehler wie den grenzüberschreitenden Zugriff auf eine Direktzugriffssequenz in der am häufigsten verwendeten Entwurfsform (in
operator[]
, dh) oder auslöst versuchen, einen ungültigen Iterator zu dereferenzieren. Die Sprache greift nicht auf den Zugriff auf ein Array außerhalb der Grenzen oder die Dereferenzierung eines Nullzeigers oder dergleichen zurück.Wenn Programmiererfehler weitgehend aus der Ausnahmebehandlungsgleichung herausgenommen werden, wird tatsächlich eine sehr große Kategorie von Fehlern entfernt, auf die andere Sprachen häufig reagieren
throwing
. C ++ tendiertassert
in solchen Fällen dazu (was nicht in Release- / Produktions-Builds kompiliert wird, sondern nur in Debug-Builds) oder nur ausfällt (häufig abstürzt), wahrscheinlich teilweise, weil die Sprache die Kosten für solche Laufzeitprüfungen nicht auferlegen möchte Dies wäre erforderlich, um solche Programmiererfehler zu erkennen, es sei denn, der Programmierer möchte die Kosten ausdrücklich durch Schreiben von Code bezahlen, der solche Überprüfungen selbst durchführt.Sutter empfiehlt sogar, in solchen Fällen Ausnahmen in C ++ - Codierungsstandards zu vermeiden:
Diese Regel ist nicht unbedingt in Stein gemeißelt. In einigen geschäftskritischeren Fällen kann es vorzuziehen sein, beispielsweise Wrapper und einen Codierungsstandard zu verwenden, der einheitlich protokolliert, wo Programmiererfehler auftreten, und
throw
bei Programmiererfehlern wie dem Versuch, etwas Ungültiges zu respektieren oder außerhalb der Grenzen darauf zuzugreifen, weil In diesen Fällen kann es zu kostspielig sein, keine Wiederherstellung durchzuführen, wenn die Software eine Chance hat. Insgesamt tendiert der häufigere Gebrauch der Sprache jedoch dazu, Programmierfehlern nicht ins Auge zu sehen.Externe Ausnahmen
Wo ich sehe, dass Ausnahmen in C ++ am häufigsten empfohlen werden (laut Standardausschuss, z. B.), handelt es sich um "externe Ausnahmen", wie in einem unerwarteten Ergebnis in einer externen Quelle außerhalb des Programms. Ein Beispiel ist, dass kein Speicher zugewiesen werden kann. Zum anderen kann eine kritische Datei nicht geöffnet werden, die für die Ausführung der Software erforderlich ist. Ein anderer kann keine Verbindung zu einem erforderlichen Server herstellen. Ein anderer ist ein Benutzer, der eine Abbruchschaltfläche blockiert, um eine Operation abzubrechen, deren allgemeiner Fallausführungspfad ohne diese externe Unterbrechung einen Erfolg erwartet. All diese Dinge liegen außerhalb der Kontrolle der unmittelbaren Software und der Programmierer, die sie geschrieben haben. Es handelt sich um unerwartete Ergebnisse aus externen Quellen, die verhindern, dass die Operation (die in meinem Buch * eigentlich als unteilbare Transaktion angesehen werden sollte) erfolgreich sein kann.
Transaktionen
Ich empfehle oft, einen
try
Block als "Transaktion" zu betrachten, da Transaktionen als Ganzes erfolgreich sein oder als Ganzes fehlschlagen sollten. Wenn wir versuchen, etwas zu tun, und es auf halbem Weg fehlschlägt, müssen alle im Programmstatus vorgenommenen Nebenwirkungen / Mutationen im Allgemeinen zurückgesetzt werden, um das System wieder in einen gültigen Zustand zu versetzen, als ob die Transaktion überhaupt nicht ausgeführt worden wäre. Ebenso wie ein RDBMS, das eine Abfrage nicht zur Hälfte verarbeitet, die Integrität der Datenbank nicht beeinträchtigen sollte. Wenn Sie den Programmstatus direkt in dieser Transaktion ändern, müssen Sie die Stummschaltung aufheben, wenn ein Fehler auftritt (und hier können Scope Guards bei RAII hilfreich sein).Die viel einfachere Alternative besteht darin , den ursprünglichen Programmstatus nicht zu ändern . Sie können eine Kopie davon mutieren und dann, wenn dies erfolgreich ist, die Kopie gegen das Original austauschen (um sicherzustellen, dass der Austausch nicht ausgelöst werden kann). Wenn dies fehlschlägt, verwerfen Sie die Kopie. Dies gilt auch dann, wenn Sie im Allgemeinen keine Ausnahmen für die Fehlerbehandlung verwenden. Eine "Transaktions" -Mentalität ist der Schlüssel zur ordnungsgemäßen Wiederherstellung, wenn vor dem Auftreten eines Fehlers Programmstatusmutationen aufgetreten sind. Es gelingt entweder als Ganzes oder scheitert als Ganzes. Es gelingt ihm nicht auf halbem Weg, seine Mutationen vorzunehmen.
Dies ist bizarrerweise eines der am seltensten diskutierten Themen, wenn ich Programmierer sehe, die nach der richtigen Fehler- oder Ausnahmebehandlung fragen, aber es ist das schwierigste von allen, in einer Software, die den Programmstatus in vielen von ihnen direkt mutieren möchte, das Richtige zu finden seine Operationen. Reinheit und Unveränderlichkeit können hier ebenso zur Erreichung der Ausnahmesicherheit beitragen wie zur Gewindesicherheit, da eine nicht auftretende Mutation / äußere Nebenwirkung nicht rückgängig gemacht werden muss.
Performance
Ein weiterer entscheidender Faktor für die Verwendung von Ausnahmen ist die Leistung, und ich meine nicht auf eine obsessive, knifflige, kontraproduktive Weise. Viele C ++ - Compiler implementieren das sogenannte "Zero-Cost Exception Handling".
Nach dem, was ich darüber gelesen habe, erfordern Ihre Ausführungspfade für allgemeine Fälle keinen Overhead (nicht einmal den Overhead, der normalerweise mit der Behandlung und Weitergabe von C-Fehlercodes einhergeht), im Gegenzug dafür, dass die Kosten stark auf die außergewöhnlichen Pfade verschoben werden ( was bedeutet,
throwing
ist jetzt teurer als je zuvor)."Teuer" ist etwas schwer zu quantifizieren, aber für den Anfang möchten Sie wahrscheinlich nicht millionenfach in eine enge Schleife werfen. Bei dieser Art von Design wird davon ausgegangen, dass Ausnahmen nicht immer links und rechts auftreten.
Nicht-Fehler
Und dieser Leistungspunkt bringt mich zu Nicht-Fehlern, was überraschend unscharf ist, wenn wir alle möglichen anderen Sprachen betrachten. Aber ich würde angesichts des oben erwähnten kostengünstigen EH-Designs sagen, dass Sie mit ziemlicher Sicherheit nicht
throw
auf einen Schlüssel reagieren möchten, der nicht in einem Set gefunden wird. Denn das ist wohl nicht nur ein Fehler (die Person, die nach dem Schlüssel sucht, hat möglicherweise das Set erstellt und erwartet, nach Schlüsseln zu suchen, die nicht immer existieren), sondern es wäre in diesem Zusammenhang auch enorm teuer.Beispielsweise möchte eine Satzkreuzungsfunktion möglicherweise zwei Sätze durchlaufen und nach Schlüsseln suchen, die sie gemeinsam haben. Wenn Sie keinen Schlüssel finden
threw
, werden Sie eine Schleife durchlaufen und möglicherweise in der Hälfte oder mehr der Iterationen auf Ausnahmen stoßen:Das obige Beispiel ist absolut lächerlich und übertrieben, aber ich habe im Produktionscode einige Leute gesehen, die aus anderen Sprachen kommen und Ausnahmen in C ++ verwenden, und ich denke, es ist eine ziemlich praktische Aussage, dass dies überhaupt keine angemessene Verwendung von Ausnahmen ist in C ++. Ein weiterer Hinweis oben ist, dass Sie feststellen werden, dass der
catch
Block absolut nichts zu tun hat und nur geschrieben wurde, um solche Ausnahmen gewaltsam zu ignorieren. Dies ist normalerweise ein Hinweis (wenn auch kein Garant), dass Ausnahmen in C ++ wahrscheinlich nicht sehr angemessen verwendet werden.Für diese Arten von Fällen ist eine Art von Rückgabewert, der auf einen Fehler hinweist (alles von der Rückkehr
false
zu einem ungültigen Iterator odernullptr
was auch immer im Kontext sinnvoll ist), normalerweise weitaus geeigneter und auch oft praktischer und produktiver als ein fehlerfreier Typ von In diesem Fall ist normalerweise kein Stapelabwicklungsprozess erforderlich, um die analogecatch
Site zu erreichen .Fragen
Das direkte Vermeiden von Ausnahmen in C ++ erscheint mir äußerst kontraproduktiv, es sei denn, Sie arbeiten in einem eingebetteten System oder in einem bestimmten Fall, der deren Verwendung verbietet (in diesem Fall müssten Sie auch alles tun, um alles zu vermeiden Bibliotheks- und Sprachfunktionalität, die sonst
throw
gerne strikt verwendet würdenothrow
new
).Wenn Sie Ausnahmen aus irgendeinem Grund unbedingt vermeiden müssen (z. B. Arbeiten über C-API-Grenzen eines Moduls, dessen C-API Sie exportieren), stimmen viele möglicherweise nicht mit mir überein, aber ich würde tatsächlich vorschlagen, einen globalen Fehlerbehandler / Status wie OpenGL mit zu verwenden
glGetError()
. Sie können dafür sorgen, dass threadlokaler Speicher verwendet wird, um einen eindeutigen Fehlerstatus pro Thread zu erhalten.Mein Grund dafür ist, dass ich es nicht gewohnt bin, Teams in Produktionsumgebungen gründlich auf mögliche Fehler zu prüfen, wenn Fehlercodes zurückgegeben werden. Wenn sie gründlich wären, könnten einige C-APIs bei nahezu jedem einzelnen C-API-Aufruf auf einen Fehler stoßen, und eine gründliche Überprüfung würde Folgendes erfordern:
... mit fast jeder einzelnen Codezeile, die die API aufruft und solche Überprüfungen erfordert. Trotzdem hatte ich nicht das Glück, mit so gründlichen Teams zusammenzuarbeiten. Sie ignorieren solche Fehler oft die Hälfte, manchmal sogar die meiste Zeit. Das ist für mich der größte Reiz von Ausnahmen. Wenn wir diese API umschließen und sie
throw
bei Auftreten eines Fehlers einheitlich gestalten , kann die Ausnahme möglicherweise nicht ignoriert werden , und meiner Ansicht nach und meiner Erfahrung nach liegt hier die Überlegenheit der Ausnahmen.Wenn jedoch keine Ausnahmen verwendet werden können, hat der globale Fehlerstatus pro Thread zumindest den Vorteil (ein großer im Vergleich zur Rückgabe von Fehlercodes an mich), dass er möglicherweise einen früheren Fehler etwas später als zu diesem Zeitpunkt abfängt trat in einer schlampigen Codebasis auf, anstatt sie völlig zu verpassen und uns völlig bewusst zu machen, was passiert ist. Der Fehler ist möglicherweise einige Zeilen zuvor oder in einem früheren Funktionsaufruf aufgetreten. Vorausgesetzt, die Software ist noch nicht abgestürzt, können wir uns möglicherweise rückwärts arbeiten und herausfinden, wo und warum er aufgetreten ist.
Ich würde nicht unbedingt sagen, dass Zeiger selten sind. In C ++ 11 und höher gibt es sogar Methoden, um auf die zugrunde liegenden Datenzeiger von Containern und ein neues
nullptr
Schlüsselwort zuzugreifen. Es wird im Allgemeinen als unklug angesehen, unformatierte Zeiger zu verwenden, um Speicher zu besitzen / zu verwalten, wenn Sieunique_ptr
stattdessen etwas verwenden können, wenn man bedenkt , wie wichtig es ist, RAII-konform zu sein, wenn Ausnahmen vorliegen. Aber rohe Zeiger, die keinen Speicher besitzen / verwalten, werden nicht unbedingt als so schlecht angesehen (selbst von Leuten wie Sutter und Stroustrup) und manchmal sehr praktisch, um auf Dinge zu verweisen (zusammen mit Indizes, die auf Dinge verweisen).Sie sind wohl nicht weniger sicher als die Standard-Container-Iteratoren (zumindest in der Version, ohne überprüfte Iteratoren), die nicht erkennen, ob Sie versuchen, sie nach ihrer Ungültigmachung zu dereferenzieren. C ++ ist immer noch unverschämt eine gefährliche Sprache, würde ich sagen, es sei denn, Ihre spezifische Verwendung möchte alles einpacken und auch nicht besitzende Rohzeiger verstecken. Mit Ausnahmen ist es fast kritisch, dass Ressourcen RAII-konform sind (was im Allgemeinen keine Laufzeitkosten verursacht), aber ansonsten wird nicht unbedingt versucht, die sicherste Sprache zu sein, um Kosten zu vermeiden, die ein Entwickler nicht explizit wünscht gegen etwas anderes tauschen. Die empfohlene Verwendung versucht nicht, Sie sozusagen vor baumelnden Zeigern und ungültigen Iteratoren zu schützen (andernfalls würden wir zur Verwendung ermutigt
shared_ptr
überall, was Stroustrup vehement ablehnt). Es versucht, Sie davor zu schützen, eine Ressource nicht ordnungsgemäß freizugeben, freizugeben, zu zerstören, freizuschalten oder zu bereinigen, wenn etwas passiertthrows
.quelle
Hier ist die Sache: Aufgrund der einzigartigen Geschichte und Flexibilität von C ++ können Sie jemanden finden, der praktisch jede Meinung zu einer Funktion verkündet, die Sie möchten. Im Allgemeinen ist die Idee jedoch umso schlimmer, je mehr das, was Sie tun, wie C aussieht.
Einer der Gründe, warum C ++ in Bezug auf Ausnahmen viel lockerer ist, ist, dass Sie nicht genau
return nil
wissen können, wann immer Sie Lust dazu haben.nil
In den allermeisten Fällen und Typen gibt es keine .Aber hier ist die einfache Tatsache. Ausnahmen erledigen ihre Arbeit automatisch. Wenn Sie eine Ausnahme auslösen, übernimmt RAII und alles wird vom Compiler verwaltet. Wenn Sie Fehlercodes verwenden, müssen Sie daran denken, diese zu überprüfen. Dies macht Ausnahmen von Natur aus wesentlich sicherer als Fehlercodes - sie überprüfen sich sozusagen selbst. Darüber hinaus sind sie ausdrucksvoller. Wenn Sie eine Ausnahme auslösen, erhalten Sie eine Zeichenfolge, die Sie über den Fehler informiert, und können sogar bestimmte Informationen enthalten, z. B. "Fehlerhafter Parameter X mit dem Wert Y anstelle von Z". Holen Sie sich einen Fehlercode von 0xDEADBEEF und was genau ist schief gelaufen? Ich hoffe sehr, dass die Dokumentation vollständig und aktuell ist, und selbst wenn Sie "Bad Parameter" erhalten, wird es Ihnen nie sagen, welcheParameter, welchen Wert es hatte und welchen Wert es haben sollte. Wenn Sie auch als Referenz fangen, können sie polymorph sein. Schließlich können Ausnahmen von Stellen ausgelöst werden, an denen Fehlercodes niemals möglich sind, wie z. B. Konstruktoren. Und wie wäre es mit generischen Algorithmen? Wie wird
std::for_each
mit Ihrem Fehlercode umgegangen? Pro-Tipp: Ist es nicht.Ausnahmen sind Fehlercodes in jeder Hinsicht weit überlegen. Die eigentliche Frage sind Ausnahmen und Behauptungen.
Hier ist das Ding. Niemand kann im Voraus wissen, welche Voraussetzungen Ihr Programm für den Betrieb hat, welche ungewöhnlichen Fehlerbedingungen vorliegen, welche im Voraus überprüft werden können und welche Fehler vorliegen. Dies bedeutet im Allgemeinen, dass Sie nicht im Voraus entscheiden können, ob ein bestimmter Fehler eine Behauptung oder eine Ausnahme sein soll, ohne die Programmlogik zu kennen. Darüber hinaus ist eine Operation, die fortgesetzt werden kann, wenn eine ihrer Unteroperationen fehlschlägt, die Ausnahme und nicht die Regel.
Meiner Meinung nach gibt es Ausnahmen zu fangen. Nicht unbedingt sofort, aber irgendwann. Eine Ausnahme ist ein Problem, von dem Sie erwarten, dass das Programm irgendwann wiederhergestellt werden kann. Die betreffende Operation kann sich jedoch niemals von einem Problem erholen, das eine Ausnahme rechtfertigt.
Assertionsfehler sind immer schwerwiegende, nicht behebbare Fehler. Gedächtnisbeschädigung, so etwas.
Wenn Sie eine Datei nicht öffnen können, handelt es sich dann um eine Behauptung oder Ausnahme? Nun, in einer generischen Bibliothek gibt es viele Szenarien, in denen der Fehler behandelt werden kann , z. B. beim Laden einer Konfigurationsdatei. Sie können stattdessen einfach einen vorgefertigten Standard verwenden, also würde ich Ausnahme sagen.
Als Fußnote möchte ich erwähnen, dass es ein "Null Object Pattern" -Ding gibt. Dieses Muster ist schrecklich . In zehn Jahren wird es der nächste Singleton sein. Die Anzahl der Fälle, in denen Sie ein geeignetes Nullobjekt erstellen können, ist winzig .
quelle
Ausnahmen wurden aus einem Grund erfunden, um zu vermeiden, dass Ihr gesamter Code so aussieht:
Stattdessen erhalten Sie etwas, das eher wie folgt aussieht, mit einem Ausnahmebehandler ganz oben
main
oder auf andere Weise günstig gelegen:Einige Leute behaupten Leistungsvorteile gegenüber dem No-Exception-Ansatz, aber meiner Meinung nach überwiegen die Lesbarkeitsbedenken geringfügige Leistungssteigerungen, insbesondere wenn Sie die Ausführungszeit für alle
if (!success)
Boilerplates berücksichtigen, die Sie überall verteilen müssen, oder das Risiko, dass Segfaults schwerer zu debuggen sind, wenn Sie dies nicht tun. Es ist relativ selten, wenn man bedenkt, dass Ausnahmen wahrscheinlich sind.Ich weiß nicht, warum Apple von der Verwendung von Ausnahmen abrät. Wenn sie versuchen, die Weitergabe nicht behandelter Ausnahmen zu vermeiden, erreichen Sie nur Leute, die stattdessen Nullzeiger verwenden, um Ausnahmen anzuzeigen. Ein Programmiererfehler führt also zu einer Nullzeigerausnahme anstelle einer viel nützlicheren Datei, bei der keine Ausnahme gefunden wurde, oder was auch immer.
quelle
In dem Beitrag, auf den Sie verweisen (Ausnahmen vs. Fehlercodes), wird meiner Meinung nach eine subtil andere Diskussion geführt. Die Frage scheint zu sein, ob Sie eine globale Liste von #define-Fehlercodes haben, wahrscheinlich komplett mit Namen wie ERR001_FILE_NOT_WRITEABLE (oder abgekürzt, wenn Sie Pech haben). Und ich denke, der Hauptpunkt in diesem Thread ist, dass ein solches prozedurales Konstrukt nicht erforderlich ist, wenn Sie in einer polymporphischen Sprache unter Verwendung von Objektinstanzen programmieren. Ausnahmen können ausdrücken, welcher Fehler einfach aufgrund ihres Typs auftritt, und sie können Informationen wie die auszudruckende Nachricht (und andere Dinge) zusammenfassen. Ich würde dieses Gespräch also als ein Gespräch darüber betrachten, ob Sie prozedural in einer objektorientierten Sprache programmieren sollten oder nicht.
Die Konversation darüber, wann und wie stark man sich bei der Behandlung von Situationen, die im Code auftreten, auf Ausnahmen verlassen muss, ist eine andere. Wenn Ausnahmen ausgelöst und abgefangen werden, führen Sie ein völlig anderes Kontrollflussparadigma ein als beim herkömmlichen Aufrufstapel. Das Auslösen von Ausnahmen ist im Grunde eine goto-Anweisung, die Sie aus Ihrem Aufrufstapel herausreißt und Sie an einen unbestimmten Ort sendet (wo immer Ihr Client-Code entscheidet, Ihre Ausnahme zu behandeln). Dies macht es sehr schwierig, über Ausnahmelogik nachzudenken .
Und so gibt es ein Gefühl wie das von Jheriko, dass diese minimiert werden sollten. Angenommen, Sie lesen eine Datei, die möglicherweise auf der Festplatte vorhanden ist oder nicht. Jheriko und Apple und diejenigen, die denken, wie sie es tun (ich selbst eingeschlossen), würden argumentieren, dass das Auslösen einer Ausnahme nicht angemessen ist - das Fehlen dieser Datei ist nicht außergewöhnlich , wird erwartet. Ausnahmen sollten den normalen Kontrollfluss nicht ersetzen. Es ist genauso einfach, wenn Ihre ReadFile () -Methode beispielsweise einen Booleschen Wert zurückgibt und der Clientcode anhand des Rückgabewerts false erkennt, dass die Datei nicht gefunden wurde. Der Client-Code kann dann mitteilen, dass die Benutzerdatei nicht gefunden wurde, oder diesen Fall leise behandeln oder was auch immer er tun möchte.
Wenn Sie eine Ausnahme auslösen, belasten Sie Ihre Kunden. Sie geben ihnen Code, der sie manchmal aus dem normalen Aufrufstapel herausreißt und sie zwingt, zusätzlichen Code zu schreiben, um zu verhindern, dass ihre Anwendung abstürzt. Eine solch mächtige und erschütternde Belastung sollte ihnen nur dann aufgezwungen werden, wenn dies zur Laufzeit oder im Interesse der Philosophie "früh scheitern" unbedingt erforderlich ist. Im ersteren Fall können Sie also Ausnahmen auslösen, wenn der Zweck Ihrer Anwendung darin besteht, einen Netzwerkbetrieb zu überwachen und jemand das Netzwerkkabel abzieht (Sie signalisieren einen katastrophalen Fehler). Im letzteren Fall können Sie eine Ausnahme auslösen, wenn Ihre Methode einen Parameter ungleich Null erwartet und Sie null erhalten (Sie signalisieren, dass Sie unangemessen verwendet werden).
Was in der objektorientierten Welt anstelle von Ausnahmen zu tun ist, haben Sie Optionen, die über das Konstrukt des prozeduralen Fehlercodes hinausgehen. Eines, das sofort in den Sinn kommt, ist, dass Sie Objekte namens OperationResult oder ähnliches erstellen können. Eine Methode kann ein OperationResult zurückgeben, und dieses Objekt kann Informationen darüber enthalten, ob die Operation erfolgreich war oder nicht, und über alle anderen Informationen, die Sie haben möchten (z. B. eine Statusmeldung, aber auch subtiler Strategien zur Fehlerbehebung enthalten) ). Wenn Sie dies zurückgeben, anstatt eine Ausnahme auszulösen, wird Polymorphismus ermöglicht, der Kontrollfluss wird beibehalten und das Debuggen wird viel einfacher.
quelle
Es gibt ein paar schöne Kapitel in Clean Code, die genau dieses Thema behandeln - abzüglich der Apple-Sichtweise.
Die Grundidee ist, dass Sie niemals Null von Ihren Funktionen zurückgeben, weil es:
Die andere begleitende Idee ist, dass Sie Ausnahmen verwenden, da dies dazu beiträgt, die Unordnung in Ihrem Code weiter zu verringern, und nicht eine Reihe von Fehlercodes erstellen muss, deren Verwaltung und Wartung (insbesondere über mehrere Module und Plattformen hinweg) und später schwierig zu verfolgen ist Wenn Ihre Kunden nur schwer entschlüsseln können, erstellen Sie stattdessen aussagekräftige Nachrichten, die die genaue Art des Problems identifizieren, das Debuggen vereinfachen und Ihren Fehlerbehandlungscode auf eine einfache
try..catch
Anweisung reduzieren können .In meinen COM-Entwicklungstagen hatte ich ein Projekt, in dem jeder Fehler eine Ausnahme auslöste und jede Ausnahme eine eindeutige Fehlercode-ID verwenden musste, die auf der Erweiterung der Windows-Standard-COM-Ausnahmen basiert. Es war ein Überbleibsel aus einem früheren Projekt, in dem zuvor Fehlercodes verwendet worden waren, aber das Unternehmen wollte alles objektorientiert machen und auf den COM-Zug springen, ohne die Art und Weise zu ändern, wie sie die Dinge zu sehr taten. Es dauerte nur 4 Monate, bis ihnen die Fehlercode-Nummern ausgegangen waren, und es lag an mir, große Codeteile zu überarbeiten, um stattdessen Ausnahmen zu verwenden. Sparen Sie sich besser den Aufwand und setzen Sie Ausnahmen mit Bedacht ein.
quelle