web-dev-qa-db-de.com

Konventionen für Ausnahmen oder Fehlercodes

Gestern hatte ich eine heftige Debatte mit einem Kollegen über die bevorzugte Fehlerberichterstattungsmethode. Hauptsächlich diskutierten wir die Verwendung von Ausnahmen oder Fehlercodes für das Melden von Fehlern zwischen Anwendungsebenen oder -modulen.

Welche Regeln verwenden Sie, um zu entscheiden, ob Sie Ausnahmen ausgeben oder Fehlercodes für die Fehlerberichterstattung zurückgeben?

96
Jorge Ferreira

Normalerweise bevorzuge ich Ausnahmen, weil sie mehr Kontextinformationen haben und den Fehler (bei ordnungsgemäßer Verwendung) deutlicher an den Programmierer weiterleiten können.

Andererseits sind Fehlercodes leichter als Ausnahmen, die Wartung ist jedoch schwieriger. Die Fehlerprüfung kann versehentlich weggelassen werden. Fehlercodes sind schwieriger zu verwalten, da Sie einen Katalog mit allen Fehlercodes führen und das Ergebnis dann einschalten müssen, um zu sehen, welcher Fehler ausgelöst wurde. Fehlerbereiche können hier hilfreich sein, denn wenn wir nur daran interessiert sind, ob ein Fehler vorliegt oder nicht, ist die Überprüfung einfacher (z. B. ein HRESULT-Fehlercode größer oder gleich 0 ist Erfolg und kleiner als null ist fehlschlag). Sie können versehentlich weggelassen werden, da es keinen programmatischen Zwang gibt, den der Entwickler auf Fehlercodes prüfen soll. Auf der anderen Seite können Sie Ausnahmen nicht ignorieren.

Zusammenfassend bevorzuge ich in fast allen Situationen Ausnahmen gegenüber Fehlercodes.

51
Jorge Ferreira

In High-Level-Sachen Ausnahmen; in Low-Level-Sachen, Fehlercodes.

Das Standardverhalten einer Ausnahme besteht darin, den Stapel aufzuheben und das Programm anzuhalten. Wenn ich ein Skript schreibe und einen Schlüssel auswähle, der sich nicht in einem Wörterbuch befindet, liegt wahrscheinlich ein Fehler vor, und ich möchte, dass das Programm anhält und mich anhält weiß alles darüber.

Wenn ich jedoch einen Code schreibe, dessen Verhalten ich in jeder möglichen Situation kennen muss , dann möchte ich Fehlercodes. Ansonsten muss ich jede Ausnahme kennen, die von jeder Zeile in meiner Funktion ausgelöst werden kann, um zu wissen, was sie tun wird (Lesen Sie Die Ausnahme, die eine Fluggesellschaft geerdet hat , um eine Vorstellung davon zu bekommen, wie schwierig dies ist). Es ist mühsam und schwierig, Code zu schreiben, der auf jede Situation angemessen reagiert (einschließlich der unglücklichen), aber das liegt daran, dass das Schreiben von fehlerfreiem Code mühsam und schwierig ist und nicht daran, dass Sie Fehlercodes übergeben.

Beide Raymond ChenndJoel haben einige beredte Argumente gegen die Verwendung von Ausnahmen für alles gemacht.

78
Tom Dunham

Ich ziehe Ausnahmen vor, weil

  • sie unterbrechen den Fluss der Logik
  • sie profitieren von der Klassenhierarchie, die mehr Funktionen/Funktionen bietet
  • bei richtiger Verwendung kann dies eine Vielzahl von Fehlern darstellen (z. B. ist eine InvalidMethodCallException auch eine LogicException, da beide auftreten, wenn in Ihrem Code ein Fehler auftritt, der vor der Laufzeit erkannt werden sollte) und
  • sie können verwendet werden, um den Fehler zu verbessern (d. h. eine FileReadException-Klassendefinition kann dann Code enthalten, um zu überprüfen, ob die Datei existiert oder gesperrt ist usw.).
22
JamShady

Fehlercodes können von den Aufrufern Ihrer Funktionen ignoriert werden (und sind es häufig!). Ausnahmen zwingen sie zumindest, den Fehler irgendwie zu behandeln. Auch wenn ihre Version des Umgangs darin besteht, einen leeren Handler (Seufzer) zu haben.

17
Maxam

Ausnahmen über Fehlercodes, kein Zweifel. Sie profitieren von den gleichen Ausnahmen wie Fehlercodes, aber auch viel mehr, ohne die Fehlercodes. Der einzige Nachteil bei Ausnahmen ist, dass es etwas mehr Aufwand ist. In der heutigen Zeit sollte dieser Aufwand jedoch für nahezu alle Anwendungen als vernachlässigbar angesehen werden.

Hier sind einige Artikel, in denen die beiden Techniken diskutiert, verglichen und verglichen werden:

Es gibt einige gute Links in denen, die Sie weiter lesen können.

15
Pistos

Ich würde nie die beiden Modelle mischen ... Es ist zu schwierig, von einem zum anderen zu konvertieren, wenn Sie von einem Teil des Stapels, der Fehlercodes verwendet, zu einem höheren Teil mit Ausnahmen wechseln.

Ausnahmen gelten für "alles, was die Methode oder Subroutine davon abhält, das zu tun, was Sie gebeten haben", um keine Meldungen über Unregelmäßigkeiten oder ungewöhnliche Umstände oder den Zustand des Systems usw. zurückzuleiten. Verwenden Sie Rückgabewerte oder ref (oder out) Parameter dafür.

Ausnahmen ermöglichen das Schreiben (und Verwenden) von Methoden mit Semantik, die von der Funktion der Methode abhängig sind, d. H. Eine Methode, die ein Employee-Objekt oder eine Liste von Employees zurückgibt, kann zu diesem Zweck typisiert werden, und Sie können es durch Aufrufen verwenden. 

Employee EmpOfMonth = GetEmployeeOfTheMonth();

Bei Fehlercodes geben alle Methoden einen Fehlercode zurück. Für diejenigen, die etwas anderes zurückgeben müssen, damit sie vom aufrufenden Code verwendet werden können, müssen Sie eine Referenzvariable übergeben, die mit diesen Daten gefüllt werden soll, und den Rückgabewert für den Fehlercode testen Fehlercode und behandeln ihn bei jedem Funktions- oder Methodenaufruf.

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

Wenn Sie so codieren, dass jede Methode nur eine einfache Sache ausführt, sollten Sie eine Ausnahme auslösen, wenn die Methode das gewünschte Ziel der Methode nicht erreichen kann. Ausnahmen sind auf diese Weise viel umfangreicher und einfacher zu verwenden als Fehlercodes. Ihr Code ist viel sauberer - Der Standardfluss des "normalen" Codepfads kann strikt dem Fall gewidmet werden, in dem die Methode IS in der Lage ist, das zu tun, was Sie wollten, und dann den Code, der bereinigt werden soll oder behandeln Sie die "außergewöhnlichen" Umstände, wenn etwas Schlimmes passiert, das den erfolgreichen Abschluss der Methode verhindert, kann der normale Code abgeschaltet werden. Wenn Sie die Ausnahme nicht behandeln können, wo sie aufgetreten ist, und den Stapel an eine Benutzeroberfläche weitergeben müssen (oder, schlimmer noch, über die Verbindung von einer Mid-Tier-Komponente zu einer Benutzeroberfläche), dann können Sie mit dem Ausnahmemodell Sie Sie müssen nicht jede intervenierende Methode in Ihrem Stack codieren, um die Ausnahme zu erkennen und an den Stack weiterzuleiten ... Das Ausnahmemodell erledigt dies automatisch für Sie ... Mit Fehlercodes kann dieses Puzzleteil sehr schnell belastet werden .

14
Charles Bretana

Es kann einige Situationen geben, in denen die Verwendung von Ausnahmen auf saubere, klare und korrekte Weise mühsam ist, aber die überwiegende Mehrheit der Ausnahmen ist die naheliegende Wahl. Der größte Vorteil der Ausnahmebehandlung gegenüber Fehlercodes besteht darin, dass der Ausführungsfluss geändert wird, was aus zwei Gründen wichtig ist.

Wenn eine Ausnahme auftritt, folgt die Anwendung nicht mehr ihrem 'normalen' Ausführungspfad. Der erste Grund, warum dies so wichtig ist, besteht darin, dass das Programm anhalten wird und nicht unvorhersehbare Dinge ausführt, es sei denn, der Autor des Codes ist ausnahmslos schlecht. Wenn ein Fehlercode nicht geprüft wird und keine geeigneten Maßnahmen als Reaktion auf einen fehlerhaften Fehlercode ergriffen werden, wird das Programm seine Arbeit fortsetzen und wer weiß, was das Ergebnis dieser Aktion sein wird. Es gibt viele Situationen, in denen die Ausführung des Programms "Was auch immer" sehr teuer werden kann. Stellen Sie sich ein Programm vor, das Leistungsdaten für verschiedene Finanzinstrumente abruft, die ein Unternehmen verkauft, und diese Informationen an Makler/Großhändler liefert. Wenn etwas schief geht und das Programm weiterläuft, kann dies fehlerhafte Leistungsdaten an die Broker und Großhändler liefern. Ich weiß nichts über andere, aber ich möchte nicht derjenige sein, der in einem VP-Büro sitzt und erklärt, warum der Kodex dazu geführt hat, dass das Unternehmen siebenstellige Bußgelder erhalten hat. Die Übermittlung einer Fehlermeldung an Kunden ist im Allgemeinen der Übermittlung falscher Daten vorzuziehen, die als "real" erscheinen könnten. Die letztere Situation lässt sich mit einem viel weniger aggressiven Ansatz wie Fehlercodes viel einfacher finden.

Der zweite Grund, warum ich Ausnahmen und deren Bruch mit der normalen Ausführung mag, ist, dass es sehr viel einfacher ist, die "normalen Dinge" -Logik von der "etwas schief gegangen" -Logik zu trennen. Für mich das:

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

... ist dem vorzuziehen:

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

Es gibt auch andere Kleinigkeiten über Ausnahmen, die Nizza sind. Wenn Sie eine Reihe von Bedingungslogiken verwenden, um zu verfolgen, ob bei einer der aufgerufenen Methoden ein Fehlercode zurückgegeben wurde, und den Fehlercode höher zurückgeben, ist dies eine Menge Boiler-Platte. In der Tat ist es eine Menge Kesselplatten, die schief gehen können. Ich habe viel mehr Vertrauen in das Ausnahmesystem der meisten Sprachen als ein Ratten-Nest mit if-else-if-else-Aussagen, die Fred geschrieben hat, und ich habe viel bessere Dinge zu tun mit meiner Zeit als Code besprach das Rattennest.

10
Dogs

In der Vergangenheit habe ich mich dem Fehlercode-Camp angeschlossen (zu viel C-Programmierung). Aber jetzt habe ich das Licht gesehen. 

Ja, Ausnahmen belasten das System. Sie vereinfachen jedoch den Code und reduzieren die Anzahl der Fehler (und WTFs).

Verwenden Sie also eine Ausnahme, aber verwenden Sie sie weise. Und sie werden dein Freund sein.

Als Anmerkung. Ich habe gelernt zu dokumentieren, welche Ausnahme mit welcher Methode ausgelöst werden kann. Leider ist dies in den meisten Sprachen nicht erforderlich. Es erhöht jedoch die Chance, mit den richtigen Ausnahmen auf der richtigen Ebene umzugehen.

9
Toon Krijthe

Sie sollten beide verwenden. Die Sache ist zu entscheiden, wann jeder verwendet werden soll .

Es gibt ein paar Szenarien, in denen Ausnahmen die naheliegende Wahl sind :

  1. In einigen Situationen können Sie mit dem Fehlercode nichts anfangen , und Sie müssen nur ihn auf einer höheren Ebene im Aufrufstapel behandeln , in der Regel nur den Fehler protokollieren, etwas anzeigen an den Benutzer oder schließen Sie das Programm. In diesen Fällen müssten Sie bei Fehlercodes die Fehlercodes manuell stufenweise in die Luft sprudeln lassen, was bei Ausnahmen natürlich viel einfacher ist. Der Punkt ist, dass dies für unerwartete und unbehandelbare Situationen ist.

  2. In Situation 1 (in der etwas Unerwartetes und Unbehandelbares passiert, willst du es einfach nicht protokollieren) können Ausnahmen hilfreich sein, weil du möglicherweise Kontextinformationen hinzufügen . Wenn ich zum Beispiel eine SqlException in meinen Datenhilfsprogrammen der unteren Ebene erhalte, möchte ich diesen Fehler auf der unteren Ebene abfangen (wo ich den SQL-Befehl kenne, der den Fehler verursacht hat), damit ich diese Informationen erfassen und erneut werfen kann mit zusätzlichen Informationen. Bitte beachten Sie das Zauberwort hier: erneut werfen und nicht schlucken . Die erste Regel zur Ausnahmebehandlung: keine Ausnahmen schlucken . Beachten Sie auch, dass mein innerer Fang nichts protokollieren muss, da der äußere Fang den gesamten Stack-Trace enthält und ihn möglicherweise protokolliert.

  3. In einigen Situationen haben Sie eine Folge von Befehlen, und wenn einer von ihnen fehlschlägt Sie sollten Ressourcen bereinigen/entsorgen (*), unabhängig davon, ob dies eine nicht behebbare Situation ist oder nicht (was sein sollte) geworfen) oder eine wiederherstellbare Situation (in diesem Fall können Sie lokal oder im Anrufercode behandeln, aber Sie brauchen keine Ausnahmen). Offensichtlich ist es viel einfacher, alle diese Befehle in einem einzigen Versuch zu platzieren, anstatt Fehlercodes nach jeder Methode zu testen und im finally-Block aufzuräumen/zu entsorgen. Bitte beachten Sie, dass wenn Sie möchten, dass der Fehler in die Luft sprudelt (und das ist wahrscheinlich das, was Sie wollen), müssen Sie ihn nicht einmal abfangen - Sie verwenden nur das Feld finally for cleanup/dispose -, das Sie nur verwenden sollten catch/retrow, wenn Sie Kontextinformationen hinzufügen möchten (siehe Punkt 2).

    Ein Beispiel wäre eine Folge von SQL-Anweisungen in einem Transaktionsblock. Auch dies ist eine "unbehandelbare" Situation, selbst wenn Sie sich dazu entschließen, sie frühzeitig zu erkennen (behandeln Sie sie lokal, anstatt nach oben zu sprudeln), ist es immer noch eine fatale Situation , in der das beste Ergebnis darin besteht, alles abzubrechen oder zumindest einen großen Teil des Prozesses abbrechen.
    (*) Dies ist wie das on error goto, das wir in alten Visual Basic verwendet haben

  4. In Konstruktoren können Sie nur Ausnahmen auslösen.

In allen anderen Situationen, in denen Informationen zurückgegeben werden, zu denen der Aufrufer eine Aktion ausführen KANN/SOLLTE , ist die Verwendung von Rückkehrcodes wahrscheinlich die bessere Alternative. Dies schließt alle erwarteten "Fehler" ein, da sie wahrscheinlich vom unmittelbaren Aufrufer behandelt werden sollten und kaum zu viele Ebenen im Stapel hochgesprudelt werden müssen.

Natürlich ist es immer möglich, erwartete Fehler als Ausnahmen zu behandeln und dann sofort eine Ebene darüber abzufangen, und es ist auch möglich, jede Codezeile in einen try catch einzubeziehen und Maßnahmen für jeden möglichen Fehler zu ergreifen. IMO, dies ist ein schlechtes Design, nicht nur, weil es viel ausführlicher ist, sondern vor allem, weil die möglichen Ausnahmen, die möglicherweise ausgelöst werden, nicht offensichtlich sind, ohne den Quellcode zu lesen - und Ausnahmen können von jeder tiefen Methode ausgelöst werden, die erzeugt + unsichtbare gotos . Sie unterbrechen die Codestruktur, indem sie mehrere unsichtbare Austrittspunkte erstellen, die das Lesen und Überprüfen von Code erschweren. Mit anderen Worten, Sie sollten niemals Ausnahmen als flow-controlverwenden, da dies für andere schwer zu verstehen und zu pflegen wäre. Es kann sogar schwierig werden, alle möglichen Codeflüsse zum Testen zu verstehen.
Nochmals: für eine korrekte Bereinigung/Entsorgung können Sie try-finally verwenden, ohne etwas zu fangen .

Die populärste Kritik an Rückkehrcodes ist, dass "jemand die Fehlercodes ignorieren könnte, aber im gleichen Sinne kann jemand auch Ausnahmen schlucken. Schlechte Ausnahmebehandlung ist einfach in beiden Methoden. Aber Das Schreiben eines guten fehlercodebasierten Programms ist immer noch viel einfacher als das Schreiben eines ausnahmebasierten Programms . Und wenn einer aus irgendeinem Grund entscheidet, alle Fehler zu ignorieren (das alte on error resume next), das können Sie ganz einfach mit Return-Codes und nicht ohne viele Try-Catchs-Boilerplate.

Die zweithäufigste Kritik an Rückkehrcodes lautet: "Es ist schwierig, in die Luft zu sprudeln" - aber das liegt daran, dass die Leute nicht verstehen, dass Ausnahmen für nicht behebbare Situationen gelten, Fehlercodes dagegen nicht.

Die Entscheidung zwischen Ausnahmen und Fehlercodes ist ein grauer Bereich. Es ist sogar möglich, dass Sie von einer wiederverwendbaren Geschäftsmethode einen Fehlercode abrufen müssen, und dann beschließen Sie, diesen Code in eine Ausnahme zu packen (möglicherweise Informationen hinzuzufügen) und ihn in die Luft sprudeln zu lassen. Es ist jedoch ein Konstruktionsfehler anzunehmen, dass ALLE Fehler als Ausnahmen geworfen werden sollten.

Etwas zusammenfassen:

  • Ich benutze gerne Ausnahmen, wenn ich eine unerwartete Situation habe, in der nicht viel zu tun ist, und normalerweise möchten wir einen großen Codeblock oder sogar die gesamte Operation oder das gesamte Programm abbrechen. Das ist wie beim alten "on error goto".

  • Ich verwende gerne Rückkehrcodes, wenn ich Situationen erwartet habe, in denen der Anrufercode eine Aktion ausführen kann/sollte. Dies umfasst die meisten Geschäftsmethoden, APIs, Validierungen usw.

Dieser Unterschied zwischen Ausnahmen und Fehlercodes ist eines der Entwurfsprinzipien der GO-Sprache, die "Panik" für schwerwiegende unerwartete Situationen verwendet, während regelmäßig erwartete Situationen als Fehler zurückgegeben werden.

Bei GO sind jedoch auch mehrere Rückgabewerte zulässig, was bei der Verwendung von Rückgabecodes sehr hilfreich ist, da Sie gleichzeitig einen Fehler und etwas anderes zurückgeben können. Unter C #/Java können wir das mit out-Parametern, Tuples oder (meinen Lieblings-) Generics erreichen, die in Kombination mit Enums dem Aufrufer eindeutige Fehlercodes liefern können:

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

Wenn ich meiner Methode eine neue mögliche Rückgabe hinzufüge, kann ich sogar alle Aufrufer überprüfen, ob sie diesen neuen Wert zum Beispiel in einer switch-Anweisung abdecken. Das kann man wirklich nicht mit Ausnahmen machen. Wenn Sie Rückkehrcodes verwenden, werden Sie in der Regel alle möglichen Fehler im Voraus kennen und auf sie testen. Mit Ausnahmen wissen Sie normalerweise nicht, was passieren könnte. Das Einschließen von Aufzählungen in Ausnahmen (anstelle von Generika) ist eine Alternative (solange klar ist, welche Art von Ausnahmen jede Methode auslöst), aber IMO ist es immer noch ein schlechtes Design.

5
drizin

Ich kann hier auf dem Zaun sitzen, aber ...

  1. Das hängt von der Sprache ab.
  2. Unabhängig davon, für welches Modell Sie sich entscheiden, sollten Sie konsistent sein, wie Sie es verwenden.

In Python ist die Verwendung von Ausnahmen üblich, und ich bin sehr glücklich, meine eigenen Ausnahmen zu definieren. In C gibt es keine Ausnahmen.

In C++ (zumindest in der STL) werden Ausnahmen normalerweise nur für wirklich außergewöhnliche Fehler geworfen (ich sehe sie selbst fast nie). Ich sehe keinen Grund, etwas anderes in meinem eigenen Code zu tun. Ja, es ist einfach, Rückgabewerte zu ignorieren, aber C++ zwingt Sie auch nicht, Ausnahmen abzufangen. Ich denke, man muss sich nur daran gewöhnen.

Die Codebasis, an der ich arbeite, ist meistens C++, und wir verwenden Fehlercodes fast überall, aber es gibt ein Modul, das Ausnahmen für jeden Fehler auslöst, einschließlich sehr ungewöhnlicher Fehler. Der Code, der dieses Modul verwendet, ist ziemlich schrecklich. Das könnte aber nur daran liegen, dass wir Ausnahmen und Fehlercodes gemischt haben. Der Code, der konsequent Fehlercodes verwendet, lässt sich viel einfacher bearbeiten. Wenn unser Code konsequent Ausnahmen verwendet, wäre es vielleicht nicht so schlimm. Das Mischen der beiden scheint nicht so gut zu funktionieren.

4
jrb

Da ich mit C++ arbeite und RAII zur sicheren Verwendung habe, verwende ich fast ausschließlich Ausnahmen. Dadurch wird die Fehlerbehandlung aus dem normalen Programmablauf herausgezogen und die Absicht klarer. 

Ich lasse jedoch Ausnahmen für außergewöhnliche Umstände. Wenn ich davon ausgehe, dass ein bestimmter Fehler sehr häufig vorkommt, überprüfe ich, ob die Operation erfolgreich ausgeführt wird, oder rufe eine Version der Funktion auf, die stattdessen Fehlercodes verwendet (wie TryParse()).

4
Eclipse

Mein Ansatz ist, dass wir sowohl Ausnahmen als auch Fehlercodes gleichzeitig verwenden können. 

Ich werde verwendet, um mehrere Arten von Ausnahmen zu definieren (z. B. DataValidationException oder ProcessInterruptExcepion), und in jeder Ausnahme wird eine detailliertere Beschreibung jedes Problems definiert.

Ein einfaches Beispiel in Java:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

Auf diese Weise verwende ich Ausnahmen, um Kategoriefehler zu definieren, und Fehlercodes, um detailliertere Informationen zum Problem zu definieren.

3
sakana

Methodensignaturen sollten Ihnen mitteilen, was die Methode bewirkt. So etwas wie long errorCode = getErrorCode (); ist möglicherweise in Ordnung, aber long errorCode = fetchRecord (); ist verwirrend.

3
Paul Croarkin

Meine Überlegung wäre, wenn Sie einen Low-Level-Treiber schreiben, der wirklich Leistung erfordert, und dann Fehlercodes verwenden. Wenn Sie diesen Code jedoch in einer übergeordneten Anwendung verwenden und etwas Overhead verarbeiten können, müssen Sie ihn mit einer Schnittstelle umschließen, die diese Fehlercodes überprüft und Ausnahmen auslöst.

In allen anderen Fällen sind Ausnahmen wahrscheinlich der Weg.

3
Claudiu

Ausnahmen gelten für außergewöhnliche Umstände, dh wenn sie nicht zum normalen Ablauf des Codes gehören.

Es ist durchaus legitim, Ausnahmen und Fehlercodes zu mischen, wobei Fehlercodes eher den Status von etwas darstellen als einen Fehler bei der Ausführung des Codes an sich (z. B. Überprüfen des Rückkehrcodes von einem untergeordneten Prozess).

Aber wenn ein außergewöhnlicher Umstand eintritt, sind Ausnahmen meines Erachtens das ausdrucksstärkste Modell.

Es gibt Fälle, in denen Sie Fehlercodes anstelle von Ausnahmen verwenden möchten oder müssen, und diese wurden bereits ausreichend behandelt (abgesehen von anderen offensichtlichen Einschränkungen wie der Compiler-Unterstützung).

Wenn Sie jedoch in die andere Richtung gehen, können Sie mithilfe von Ausnahmen noch höhere Abstraktionen für Ihre Fehlerbehandlung erstellen, um Ihren Code noch ausdrucksvoller und natürlicher zu gestalten. Ich würde wärmstens empfehlen, diesen exzellenten, aber unterschätzten Artikel von C++ - Experten Andrei Alexandrescu über das, was er "Enforcements" nennt, zu lesen: http://www.ddj.com/cpp/184403864 . Obwohl es sich um einen C++ - Artikel handelt, sind die Prinzipien allgemein anwendbar, und ich habe das Durchsetzungskonzept ziemlich erfolgreich in C # übersetzt.

2
philsquared

Erstens stimme ich Toms answer zu, dass Ausnahmen für High-Level-Ausnahmen verwendet werden und für Low-Level-Ausfälle Fehlercodes verwendet werden, sofern dies nicht die serviceorientierte Architektur (SOA) ist.

In einer SOA, in der Methoden auf verschiedenen Maschinen aufgerufen werden können, werden möglicherweise keine Ausnahmen über den Draht geleitet. Stattdessen verwenden wir Erfolgs-/Fehlerantworten mit einer Struktur wie folgt (C #):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

Und wie folgt verwenden:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

Wenn diese konsistent in Ihren Service-Antworten verwendet werden, entsteht ein sehr schönes Muster für die Behandlung von Erfolgen/Fehlern in der Anwendung. Dies ermöglicht eine einfachere Fehlerbehandlung bei asynchronen Aufrufen innerhalb von Diensten sowie zwischen Diensten.

2
orad

Ich würde Ausnahmen für alle Fehlerfälle vorziehen, es sei denn, ein Fehler ist ein zu erwartendes fehlerfreies Ergebnis einer Funktion, die einen primitiven Datentyp zurückgibt. Z.B. Wenn Sie den Index eines Teilstrings innerhalb einer größeren Zeichenfolge suchen, wird normalerweise -1 zurückgegeben, wenn keine NotFoundException ausgelöst wird.

Die Rückgabe ungültiger Zeiger, die möglicherweise dereferenziert sind (z. B. NullPointerException in Java verursachen), ist nicht zulässig.

Die Verwendung mehrerer verschiedener numerischer Fehlercodes (-1, -2) als Rückgabewerte für die gleiche Funktion ist in der Regel ein ungünstiger Stil, da Clients möglicherweise eine Prüfung "== -1" anstelle von "<0" durchführen.

Hierbei ist zu beachten, dass sich die APIs im Laufe der Zeit weiterentwickeln. Eine gute API ermöglicht das Ändern und Erweitern des Fehlerverhaltens auf verschiedene Arten, ohne die Clients zu brechen. Z.B. Wenn ein Client-Fehlerhandle auf 4 Fehlerfälle geprüft wurde und Sie einen fünften Fehlerwert zu Ihrer Funktion hinzufügen, kann der Client-Handler dies möglicherweise nicht testen und unterbrechen. Wenn Sie Ausnahmen auslösen, wird es für Clients normalerweise einfacher, auf eine neuere Version einer Bibliothek zu migrieren.

Eine weitere Sache, die zu beachten ist, ist die Arbeit in einem Team, wo alle Entwickler eine klare Linie ziehen müssen, um eine solche Entscheidung zu treffen. Z.B. "Ausnahmen für Dinge auf hoher Ebene, Fehlercodes für Dinge auf niedriger Ebene" ist sehr subjektiv.

In jedem Fall, in dem mehr als ein trivialer Fehlertyp möglich ist, sollte der Quellcode niemals das numerische Literal verwenden, um einen Fehlercode zurückzugeben oder ihn zu behandeln (Rückgabe -7, wenn x == -7 ...), aber immer eine benannte Konstante (Rückgabe NO_SUCH_FOO, wenn x == NO_SUCH_FOO).

1
tkruse

Wenn Sie unter großen Projekten arbeiten, können Sie nicht nur Ausnahmen oder nur Fehlercodes verwenden. In verschiedenen Fällen sollten Sie unterschiedliche Ansätze verwenden.

Beispielsweise entscheiden Sie, nur Ausnahmen zu verwenden. Sobald Sie sich jedoch für die asynchrone Ereignisverarbeitung entschieden haben. Es ist keine gute Idee, in diesen Situationen Ausnahmen für die Fehlerbehandlung zu verwenden. Aber Fehlercodes überall in der Anwendung zu verwenden, ist langweilig.

Meiner Meinung nach ist es normal, sowohl Ausnahmen als auch Fehlercodes gleichzeitig zu verwenden.

1
gomons

Meine allgemeine Regel lautet:

  • In einer Funktion kann nur ein Fehler auftreten: Verwenden Sie den Fehlercode (als Parameter der Funktion).
  • Es kann mehr als ein bestimmter Fehler auftreten: Ausnahme auslösen
0
DEADBEEF

Für die meisten Anwendungen sind Ausnahmen besser. Die Ausnahme ist, wenn die Software mit anderen Geräten kommunizieren muss. Die Domäne, in der ich arbeite, ist industrielle Kontrollen. Hier werden Fehlercodes bevorzugt und erwartet. Meine Antwort ist also, dass es von der Situation abhängt.

0
Jim C

Ich denke, es hängt auch davon ab, ob Sie wirklich Informationen wie Stack-Trace vom Ergebnis benötigen. Wenn ja, entscheiden Sie sich definitiv für Exception, die das Objekt mit vielen Informationen zum Problem versorgen. Wenn Sie sich jedoch nur für das Ergebnis interessieren und sich nicht dafür interessieren, warum dieses Ergebnis dann den Fehlercode wählt. 

z.B. Wenn Sie Dateien und IOException verarbeiten, kann der Client wissen, wo er ausgelöst wurde, das Öffnen von Dateien oder das Analysieren von Dateien usw. Besser ist es, wenn Sie IOException oder ihre spezifische Unterklasse zurückgeben. Ein Szenario wie Sie haben jedoch eine Anmeldemethode, und Sie möchten wissen, ob es erfolgreich war oder nicht. Dort geben Sie entweder nur Boolean zurück oder zeigen eine korrekte Meldung an und geben Fehlercode zurück. Der Kunde ist nicht daran interessiert zu wissen, welcher Teil der Logik den Fehlercode verursacht hat. Er weiß nur, ob sein Credential ungültig ist oder ein Kontosperre usw.

Eine andere Anwendung, die ich mir vorstellen kann, ist, wenn Daten im Netzwerk übertragen werden. Ihre entfernte Methode kann anstelle von Exception nur Fehlercode zurückgeben, um die Datenübertragung zu minimieren. 

0
ashah