it-swarm.com.de

Warum können Sie in Programmiersprachen keinen try-Block ohne catch-Block verwenden?

Warum fordern Sprachen Fangblöcke, wenn sie nicht benötigt werden?

Der Compiler oder Parser beschwert sich mit diesem Code:

try {
    const utils = require("applicationutils");
}

Aber mit diesem Code ist es in Ordnung:

try {
    const utils = require("applicationutils");
} catch(e) {}

Ich brauche den Fangblock nicht.

Ich benutze JavaScript, wenn es darauf ankommt.

Beispielcode aktualisieren :

// setting defaults ahead of try - no need for a catch block
var setting = 10;
var myRegEx = "/123/g";
var supportsRegEx2 = false;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = "/123/gm";
    supportsRegEx2 = true;
}

Die lange Geschichte
Ich arbeite in einer browserähnlichen Umgebung, in der häufig neue APIs eingeführt werden. Einige APIs werden stillschweigend eingeführt, es ist jedoch keine Dokumentation verfügbar.

Wenn ich eine neue API verwenden möchte, kann ich in meinem Manifest ein Flag für die Mindestversion setzen. Wenn ich jedoch ein Minimum festlege, schließt dies jeden vor dieser Version aus.

Ich habe E-Mails von Benutzern erhalten, die aus verschiedenen Gründen kein Upgrade durchführen können. Einige verwenden frühere Versionen, nur weil sie nicht aktualisiert wurden, andere aus büropolitischen Gründen.

Ich kenne Unternehmen, die beim letzten Überprüfen immer noch IE6 verwenden. Einige Male sind die Systemanforderungen gestiegen, wobei Hardware der vorherigen Generation ausgeschlossen ist.

Ich hätte feststellen können, wann eine API eingeführt wurde, und anhand einer Versionsnummer prüfen können, oder ich könnte versuchen, die Klasse einzuschließen, um später ein Unterstützungsflag zu überprüfen oder zu überprüfen, ob die Klasse nicht null ist.

Da das Festlegen einer Mindestversion auf diese Weise einen Teil der Zielgruppe ausschließen würde, könnte ich die Benutzer unterstützen, die nicht aktualisiert wurden, und gleichzeitig Benutzern, die über neuere APIs Zugriff auf die Funktionen haben, den Zugriff gewähren.

Ansatz 1:

const system = require("system");

try {
   const foo = require("foo");
}

function performSomeAction() {
    if (supportsFoo) {
       foo.bar();
    }
}

Ansatz 2:

const system = require("system");
var supportsFoo = false;

try {
   const foo = require("foo");
   supportsFoo = true;
}

function start() {
    if (foo) {
       // do something
    }
}

In meinen Fällen kann ich nicht sehen, dass ein Fangblock notwendig ist.

Semantik :

Für meinen speziellen Fall:

Try to import a class using require() and
set a constant or variable to that class / api
If an error is thrown skip any other code in the try block and continue
If no error the constant or variable will not be null
In the constructor check for not-null and enable features for use

Per Kommentar unten ist Testcode in JS-Umgebung:

var x = function() {
    try {
        console.log("hello")
        throw new Error();
        console.log("world");
    }
    // catch(e) {}
    console.log("After try");
}

// VM373:6 Uncaught SyntaxError: Missing catch or finally after try

EIN ANDERER GEBRAUCHSFALL (5 Tage später) :
FWIW in CSS gibt es die Idee eines progressive enhancement.

Aufgrund der Art und Weise, wie CSS-Stile definiert werden, können Stile mehrfach definiert und Stile mit demselben Namen hinzugefügt werden, die zuletzt hinzugefügt wurden.

So können Sie diese Liste von Stilen wie folgt haben:

body {
   color: red;
   color: blue;
}

Die Farbe ist blau, da sie zuletzt definiert wurde. Das ist in CSS vollkommen gültig. Es ist nicht richtig oder falsch, es ist gültig.

Dies ist praktisch, wenn Sie progressive Funktionen unterstützen möchten, ohne die Unterstützung für frühere Browser zu beeinträchtigen:

.slideshow {
    display: flex;
    display: grid;
}

In der obigen Stildeklaration verwendet der Browser eine grid Anzeige, wenn dies unterstützt wird, und wenn nicht, verwendet er eine flex Anzeige. Es wird kein Fehler ausgegeben, wenn ein falscher Wert verwendet wird.

Das ist das gleiche wie:

var element = document.getElementById("label");
element.style.setPropertyValue("flex");

try {
    // if the style is not supported the browser retains the value flex
    element.style.setPropertyValue("display", "grid");
} catch(e){ /* no catch is needed */ }

Sie können das CSS auch wie folgt schreiben:

.slideshow {
    display: grid;
}

@supports (display: grid) {
    .slideshow {
        display: grid;
    }
}

Der Code dafür wäre:

var element = document.getElementById("label");
element.style.setPropertyValue("display", "flex");

if (CSS.supports("('display:grid')")) {
    element.style.setPropertyValue("display", "grid");
}

In beiden Fällen definieren Sie eine Variable und versuchen, test/set auf einen neuen Wert zu setzen, den sie möglicherweise nicht unterstützt.

Der erste Ansatz wird aus Gründen der größtmöglichen Abwärtskompatibilität empfohlen:

.slideshow {
    display:flex;
    display:grid;
}

Zugegeben, wenn Sie nicht kompatible Stile festlegen, behält der Browser die zuvor gültigen Werte bei. Ich vertraue auf dieses Wissen oder diese Informationen, um festzustellen, dass ein Fangblock nicht erforderlich ist. Dies ist übrigens nicht mein Anwendungsfall. Mein Anwendungsfall befindet sich im Abschnitt "Lange Geschichte".

7
1.21 gigawatts

Ich brauche den Fangblock nicht.

Aber du musst fangen. Das Verhalten Ihres Codes mit einem catch-Block besteht darin, eine Ausnahme abzufangen und dann zu vergessen, dass dies geschehen ist. Jede Ausnahme, die versucht, durchzugehen, wird beendet, und Ihr Code gibt im Grunde vor, dass der Block try erfolgreich ausgeführt wurde.

Sie möchten also, dass ein nackter try -Block so wirkt, als würde er eine Ausnahme abfangen und sie ignorieren. Hier ist die Sache: Ein Standardfall soll ein häufiger Fall sein, der von vielen Benutzern nützlich und nicht fehleranfällig ist. if erfordert normalerweise kein else, da es viele Fälle gibt, in denen Sie nichts zu tun haben.

Ich weiß nichts darüber, warum Sie Ausnahmen auf den Boden fallen lassen und so tun wollen, als wären sie nicht aufgetreten. Ich bin bereit zu akzeptieren, dass Sie eine gute Rechtfertigung dafür haben. Tatsache ist jedoch, dass es im allgemeiner Fall keine gute Idee ist. Die meisten Programmierer wollen das nicht, und es gibt gute Argumente dafür, dass es im Allgemeinen unklug ist, so etwas zu tun.

Mit einer guten Sprache können Sie etwas Unkluges tun. Aber eine gute Sprache lässt Sie nicht etwas Unkluges tun aus Versehen. Da das gewünschte Verhalten im Allgemeinen unklug ist, werden Sie in Sprachen häufig explizit dazu aufgefordert.

60
Nicol Bolas

Wie andere bereits betont haben, gibt es gute Gründe, warum ein einfaches try in JavaScript oder in den meisten anderen Sprachen mit derselben Syntax nicht zulässig ist:

  • Es ist nicht einmal klar, was es tun soll: Aus Gründen der Konsistenz mit try...finally sollte ein try ohne catch wohl nur Ausnahmen durchlassen ohne sie zu fangen. Aber ohne einen catch oder einen finally Block wäre ein solcher einfacher try nutzlos.

  • Höchstwahrscheinlich ist ein try ohne catch (oder ein finally) nur ein Bearbeitungsfehler, genau wie ein else ohne if. Es ist gut, wenn der Parser solche Fehler bemerkt und Sie darüber informiert.

  • Ausnahmen zu fangen und sie vollständig zu ignorieren ist eine ungewöhnliche und riskante Sache, da es einfach ist, mehr zu fangen, als Sie erwartet haben. Wenn Sie es wirklich wollen, ist es gut, dass die Sprache Sie zumindest dazu zwingt, explizit darüber zu sprechen.

Beachten Sie insbesondere, dass Ihr Beispielcode etwas fragil ist und es einfach macht, subtile Fehler einzuführen, die schwer zu erkennen und zu debuggen sind, da alles erscheint. ) sich normal zu verhalten. Nehmen wir zum Beispiel die folgende scheinbar harmlose Änderung an Ihrem Beispiel vor:

// setting defaults ahead of try - no need for a catch block?
var setting = 10;
var myRegEx = optimizeRegEx("/123/g");
var supportsRegEx2 = false;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}
catch (e) {} // why does JS force me to do this?

Können Sie sehen, was hier schief gehen könnte? Ich gebe dir einen Moment, um darüber nachzudenken ...

OK, was passiert, wenn optimizeRegEx() einen Fehler hat und abstürzt, wenn die Eingabe "/123/gm" Gegeben wird? Diese Ausnahme wird auch abgefangen und ignoriert, da sie im Block try auftritt, sodass Sie sie nie sehen werden! Schlimmer noch, jetzt werden Ihre Variablen in einem inkonsistenten Zustand enden: setting wird 20 sein und das Modul applicationutils wird geladen, aber myRegEx hat immer noch seinen ursprünglichen Wert und supportsRegEx2 Ist falsch. Viel Spaß beim Debuggen!

Wie würden Sie das sicher machen? Nun, das erste wäre, sicherzustellen, dass die Variablen immer zumindest in einem konsistenten Zustand enden. Eine Möglichkeit, dies zu tun, besteht darin, sie im Block catch auf ihre Standardeinstellungen (zurück) zu setzen:

// these will be set inside the try/catch block below
var setting, myRegEx, supportsRegEx2;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}
catch (e) {
    // something went wrong, probably require() failed: use fallback defaults instead
    setting = 10;
    myRegEx = optimizeRegEx("/123/g");
    supportsRegEx2 = false;
}

Das ist ein bisschen besser: Ausnahmen von optimizeRegEx("/123/gm") werden weiterhin stillschweigend weggeworfen, aber in diesem Fall erhalten zumindest alle Variablen ihre Standardwerte.

Noch besser wäre es, den Code so zu korrigieren, dass nur die spezifische Ausnahme abfängt, die ausgelöst wird, wenn require() fehlschlägt. Leider macht JavaScript dies schwieriger als es sein sollte, da es keine bedingten catch -Klauseln unterstützt. Wir können jedoch zumindest dem allgemeinen Prinzip folgen, so wenig Code wie möglich in jedem try zu haben, um das Abfangen unerwarteter Ausnahmen zu vermeiden, und wir können auch die abgefangene Ausnahme im catch -Block untersuchen und erneut werfen wenn es nicht das ist, was wir erwartet hatten:

// setting defaults ahead of try
var setting = 10;
var myRegEx = optimizeRegEx("/123/g");
var supportsRegEx2 = false;

// try loading utils, catch and ignore module loading error
var utils = null;
try {
    utils = require("applicationutils");
}
catch (e) {
    if (e.code !== 'MODULE_NOT_FOUND') throw e;  // rethrow any unexpected errors
}

// adjust variables if the module loaded without errors
if (utils !== null) {
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}

(Fehlercodetest basierend auf diese Antwort auf SO .)

Jetzt ignorieren wir nur die Fehler, die aus der Aussage stammen, dass wir erwarten, dass sie auslöst, und die auch aussehen wie der Fehler, den wir erwarten. Dies minimiert das Risiko, dass wir versehentlich einen Fehler ignorieren, der nicht war, den wir erwartet hatten, und macht den Code daher robuster und einfacher zu debuggen .


In Bezug auf Ihren tatsächlichen Anwendungsfall (jetzt, wo Sie ihn beschrieben haben) scheint es mir, dass Sie wirklich eine Wrapper-Funktion wie diese wollen:

function tryRequire(moduleName) {
    try {
        return require(moduleName);
    }
    catch (e) {
        if (e.code === 'MODULE_NOT_FOUND') return null;  // adjust this as needed to detect module loading errors in your environment
        throw e;  // rethrow any unexpected errors
    }
}

Auf diese Weise können Sie einfach const foo = tryRequire('foo') ausführen und dann überprüfen, ob das Modul erfolgreich geladen wurde, basierend darauf, ob foo null ist.

33
Ilmari Karonen

Viele Sprachen erlauben: try { ... } finally { ... } Oder eine Variante. Nehmen Sie als Beispiel C #. Es gibt viele andere.

Aber ein try { ... } Hat keinen Sinn. Es hat keine Bedeutung ohne ein verbundenes:

  • catch(...) { ... }
  • finally { ... }
  • catch(...) { ... } finally { ... }.
  • catch(...) { ... } catch(...) { ... } ... catch(...) { ... }.

Der Anwendungsfall, den Sie hervorheben, ist tatsächlich sehr schlechter Code.

Wenn Sie einen Fehler erwarten und nichts dagegen unternehmen, ist Ihr Design grundlegend fehlerhaft.

Man könnte sagen, dass diese Syntax wie erwartet funktioniert. Weil es Sie genug irritiert, nach einer Lösung zu suchen.

Die Lösung besteht darin, entweder das try insgesamt zu entfernen oder herauszufinden, wie mit dem Fehler umgegangen werden soll.

9
Kain0_0

Was würde das bedeuten?

Ich würde erwarten:

try {
    const utils = require("applicationutils");
}

Um dasselbe zu bedeuten wie:

const utils = require("applicationutils");

try/catch/finally ist ein gut verstandenes Muster, das in vielen verschiedenen Sprachen verwendet wird. Ausnahmen implizieren naturgemäß, dass es irgendwo anders einen Wiederherstellungshandler gibt.

Es hört sich so an, als ob Sie nach VBs On Error Resume Next suchen, das Fehler zeilenweise abfängt und ignoriert. Heutzutage wird dies allgemein als schlechte Praxis angesehen, da dies bedeutet, dass keine tatsächliche Fehlerbehandlung stattfindet. Der Ablauf wird fortgesetzt, als ob die vorherige Zeile erfolgreich gewesen wäre, was zu einer Eskalation des Müll-/Schadensniveaus führen kann, das durch schlechte Eingaben verursacht wird. anstatt nur die schlechte Eingabe anzuhalten oder zu beseitigen.

Das Try/Catch-Muster ist nützlich, da es Ihren Algorithmus in Teile unterteilen kann, in denen Sie fehlschlagen, aber wiederherstellen können, und in Teile, in denen nach einem Fehler keine Wiederherstellung erfolgt. Und für den Fall, dass nach einem Fehler keine Wiederherstellung erfolgt, kann der Teil that den Rest der Anwendung nicht fortsetzen.

Wenn Sie dies in js benötigen, können Sie den gleichen Effekt erzielen, indem Sie eine Funktion erstellen, die eine Funktion als Argument verwendet und sie in eine Ausführung in einem try catch-Block einschließt, in dem nur ein Fehlerobjekt festgelegt wird. Sie können dann Lambda-Ausdrücke für jede Zeile verwenden, in der Sie als Nächstes fortfahren möchten.

var err = {}
var ex = function(action){
             try {
                 action()
             }
             catch (e) {
                 err=e
             }
         }
 var i =5
 ex(t=> alert(i.foo()))
 console.log(i)
 console.log(err.message)

Obwohl es nicht mit Zeilen funktioniert, die Variablen deklarieren und initialisieren.

4
jmoreno

Nein, keine Sprache unterstützt diese Syntax. Das nächste, das ich kenne, ist VBs ON ERROR RESUME NEXT welche Art von macht was Sie wollen: Es ignoriert jede Ausnahme und fährt einfach mit der nächsten Zeile fort.

Warum unterstützen Sprachen dies nicht? Da es sehr selten ist, benötigen Sie es in einem gut gestalteten Programm. Normalerweise würde ein fehlender Haken oder schließlich darauf hinweisen, dass der Programmierer ihn vergessen hat, daher ist die Syntax ein Schutz.

In dem sehr seltenen Fall, dass Sie Ausnahmen einfach ignorieren möchten, können Sie einen leeren Catch-Block mit einem Kommentar verwenden, der erklärt, warum Sie hier Ausnahmen ignorieren. Dies signalisiert die Absichten viel besser. Wenn Sie etwas wirklich Ungewöhnliches und potenziell Gefährliches tun, ist es ein guter Stil, im Code klar zu kommunizieren, dass Sie wissen, was Sie tun (und warum).

3
JacquesB

Swift ermöglicht den Versuch ohne Fang. Es gibt vier Möglichkeiten in Swift, um Ausnahmen zu behandeln: 1. Die übliche Methode mit try and catch. 2. Durch Verwendung von "try?" Bei einem Methodenaufruf, der ein Ergebnis vom Typ T zurückgibt, was bedeutet Sie erhalten ein Ergebnis vom Typ Optional, und das Ergebnis ist Null, wenn der Aufruf eine Ausnahme ausgelöst hat. 3. Verwenden Sie "try!", was bedeutet, dass Ihre Anwendung abstürzt, wenn eine Ausnahme ausgelöst wird, und 4. indem Sie einen Aufruf in a tätigen Methode, die selbst wirft.

2
gnasher729