it-swarm.com.de

Kurzschlussbewertung, ist es eine schlechte Praxis?

Etwas, das ich seit einiger Zeit kenne, aber nie in Betracht gezogen habe, ist, dass es in den meisten Sprachen möglich ist, Operatoren in einer if-Anweisung basierend auf ihrer Reihenfolge Vorrang einzuräumen. Ich benutze dies oft, um Nullreferenzausnahmen zu verhindern, z.

if (smartphone != null && smartphone.GetSignal() > 50)
{
   // Do stuff
}

In diesem Fall führt der Code dazu, dass zuerst überprüft wird, ob das Objekt nicht null ist, und dann dieses Objekt verwendet wird, um zu wissen, dass es vorhanden ist. Die Sprache ist clever, weil sie weiß, dass es keinen Sinn macht, die zweite Anweisung zu bewerten, wenn die erste Anweisung falsch ist, und daher niemals eine Nullreferenzausnahme ausgelöst wird. Dies funktioniert genauso für die Operatoren and und or.

Dies kann auch in anderen Situationen nützlich sein, z. B. wenn überprüft wird, ob sich ein Index innerhalb der Grenzen eines Arrays befindet und eine solche Technik meines Wissens in verschiedenen Sprachen ausgeführt werden kann: Java, C #, C++, Python und Matlab.

Meine Frage lautet: Ist diese Art von Code eine Darstellung einer schlechten Praxis? Entsteht diese schlechte Praxis aus einem versteckten technischen Problem (dh dies könnte schließlich zu einem Fehler führen) oder führt sie zu einem Lesbarkeitsproblem für andere Programmierer? Könnte es verwirrend sein?

89
innova

Nein, das ist keine schlechte Praxis. Sich auf das Kurzschließen von Bedingungen zu verlassen, ist eine allgemein akzeptierte und nützliche Technik - solange Sie eine Sprache, die dieses Verhalten garantiert (die die überwiegende Mehrheit der modernen Sprachen umfasst) verwenden.

Ihr Codebeispiel ist ziemlich klar und in der Tat ist dies oft der beste Weg, es zu schreiben. Alternativen (wie verschachtelte if -Anweisungen) wären chaotischer, komplizierter und daher schwerer zu verstehen.

Ein paar Einschränkungen:

Verwenden Sie gesunden Menschenverstand in Bezug auf Komplexität und Lesbarkeit

Folgendes ist keine so gute Idee:

if ((text_string != null && replace(text_string,"$")) || (text_string = get_new_string()) != null)

Wie bei vielen "cleveren" Methoden zum Reduzieren der Zeilen in Ihrem Code kann eine übermäßige Abhängigkeit von dieser Technik zu schwer verständlichem, fehleranfälligem Code führen.

Wenn Sie mit Fehlern umgehen müssen, gibt es oft einen besseren Weg

Ihr Beispiel ist in Ordnung, solange es ein normaler Programmstatus für das Smartphone ist, der null ist. Wenn dies jedoch ein Fehler ist, sollten Sie eine intelligentere Fehlerbehandlung einbeziehen.

Vermeiden Sie wiederholte Überprüfungen derselben Bedingung

Wie Neil betont, sind viele wiederholte Überprüfungen derselben Variablen unter verschiedenen Bedingungen höchstwahrscheinlich ein Beweis für einen Konstruktionsfehler. Wenn Sie zehn verschiedene Anweisungen in Ihrem Code haben, die nicht ausgeführt werden sollten, wenn smartphonenull ist, prüfen Sie, ob es einen besseren Weg gibt, dies zu handhaben, als die Variable jedes Mal zu überprüfen.

Hier geht es nicht wirklich speziell um Kurzschlüsse. Es ist ein allgemeineres Problem des sich wiederholenden Codes. Es ist jedoch erwähnenswert, da es häufig vorkommt, dass Code viele wiederholte Anweisungen enthält, wie z. B. Ihre Beispielanweisung.

125
user82096

Angenommen, Sie haben eine Sprache im C-Stil ohne && Verwendet und mussten den entsprechenden Code wie in Ihrer Frage ausführen.

Ihr Code wäre:

if(smartphone != null)
{
  if(smartphone.GetSignal() > 50)
  {
    // Do stuff
  }
}

Dieses Muster würde viel auftauchen.

Stellen Sie sich nun vor, Version 2.0 unserer hypothetischen Sprache führt && Ein. Überlegen Sie, wie cool Sie es finden würden!

&& Ist das anerkannte, idiomatische Mittel, um das zu tun, was mein Beispiel oben tut. Es ist keine schlechte Praxis, es zu verwenden, in der Tat ist es eine schlechte Praxis, es in Fällen wie den oben genannten nicht zu verwenden: Ein erfahrener Codierer in einer solchen Sprache würde sich fragen, wo das else war oder ein anderer Grund, Dinge in der nicht zu tun normale Mode.

Die Sprache ist clever, weil sie weiß, dass es keinen Sinn macht, die zweite Anweisung zu bewerten, wenn die erste Anweisung falsch ist, und daher niemals eine Nullreferenzausnahme ausgelöst wird.

Nein, d sind schlau, weil du wusstest, dass es keinen Sinn macht, die zweite Aussage zu bewerten, wenn die erste Aussage falsch ist. Die Sprache ist dumm wie eine Schachtel Steine ​​und hat getan, was Sie ihm gesagt haben. (John McCarthy war noch schlauer und erkannte, dass eine Kurzschlussbewertung für Sprachen nützlich sein würde).

Der Unterschied zwischen Ihnen als klug und der Sprache als klug ist wichtig, da gute und schlechte Praktiken oft darauf zurückzuführen sind, dass Sie so klug wie nötig sind, aber nicht klüger.

Erwägen:

if(smartphone != null && smartphone.GetSignal() > ((range = (range != null ? range : GetRange()) != null && range.Valid ? range.MinSignal : 50)

Dies erweitert Ihren Code, um ein range zu überprüfen. Wenn range null ist, versucht es, es durch einen Aufruf von GetRange() zu setzen, obwohl dies möglicherweise fehlschlägt, sodass range möglicherweise immer noch null ist. Wenn der Bereich danach nicht null ist und Valid ist, wird die Eigenschaft MinSignal verwendet, andernfalls wird der Standardwert 50 Verwendet.

Dies hängt auch von && Ab, aber es ist wahrscheinlich zu klug, es in eine Zeile zu setzen (ich bin nicht 100% sicher, ob ich es richtig habe, und ich werde es nicht noch einmal überprüfen, weil diese Tatsache meinen Standpunkt demonstriert ).

Es ist zwar nicht &&, Das hier das Problem ist, aber die Fähigkeit, damit viel in einen Ausdruck zu setzen (eine gute Sache), erhöht unsere Fähigkeit, schwer verständliche Ausdrücke unnötig zu schreiben (eine schlechte Sache). .

Ebenfalls:

if(smartphone != null && (smartphone.GetSignal() == 2 || smartphone.GetSignal() == 5 || smartphone.GetSignal() == 8 || smartPhone.GetSignal() == 34))
{
  // do something
}

Hier kombiniere ich die Verwendung von && Mit einer Überprüfung auf bestimmte Werte. Dies ist bei Telefonsignalen nicht realistisch, tritt jedoch in anderen Fällen auf. Hier ist es ein Beispiel dafür, dass ich nicht klug genug bin. Wenn ich Folgendes getan hätte:

if(smartphone != null)
{
  switch (smartphone.GetSignal())
  {
    case 2: case 5: case 8: case 34:
      // Do something
      break;
  }
}

Ich hätte sowohl an Lesbarkeit als auch an Leistung gewonnen (die mehrfachen Aufrufe von GetSignal() wären wahrscheinlich nicht wegoptimiert worden).

Auch hier ist das Problem nicht so sehr &&, Sondern diesen bestimmten Hammer zu nehmen und alles andere als Nagel zu sehen; Wenn ich es nicht benutze, kann ich etwas besser machen als wenn ich es benutze.

Ein letzter Fall, der von den Best Practices abweicht, ist:

if(a && b)
{
  //do something
}

Verglichen mit:

if(a & b)
{
  //do something
}

Das klassische Argument, warum wir Letzteres bevorzugen könnten, ist, dass es einen Nebeneffekt bei der Beurteilung von b gibt, den wir wollen, ob a wahr ist oder nicht. Da bin ich anderer Meinung: Wenn dieser Nebeneffekt so wichtig ist, lassen Sie ihn in einer separaten Codezeile geschehen.

In Bezug auf die Effizienz dürfte jedoch eines der beiden besser sein. Der erste führt offensichtlich weniger Code aus (wobei b überhaupt nicht in einem Codepfad bewertet wird), wodurch wir die Zeit sparen können, die für die Bewertung von b erforderlich ist.

Der erste hat aber noch einen Zweig. Überlegen Sie, ob wir es in unserer hypothetischen Sprache im C-Stil ohne && Neu schreiben:

if(a)
{
  if(b)
  {
    // Do something
  }
}

Dieses zusätzliche if ist in unserer Verwendung von && Versteckt, aber es ist immer noch da. Und als solches ist es ein Fall, in dem eine Verzweigungsvorhersage stattfindet und daher möglicherweise eine Verzweigungsfehlvorhersage vorliegt.

Aus diesem Grund kann der Code if(a & b) in einigen Fällen effizienter sein.

Hier würde ich sagen, dass if(a && b) immer noch der Best-Practice-Ansatz ist: Häufiger der einzige, der in einigen Fällen praktikabel ist (wobei b fehlerhaft ist, wenn a ist falsch) und meistens schneller. Es ist jedoch erwähnenswert, dass if(a & b) in bestimmten Fällen häufig eine nützliche Optimierung darstellt.

26
Jon Hanna

Sie können dies weiterführen, und in einigen Sprachen ist dies die idiomatische Methode, um Dinge zu tun: Sie können die Kurzschlussbewertung auch außerhalb von bedingten Anweisungen verwenden, und sie werden selbst zu einer Form von bedingten Anweisungen. Z.B. In Perl ist es für Funktionen idiomatisch, etwas Falsches beim Scheitern (und etwas Wahres beim Erfolg) und so etwas zurückzugeben

open($handle, "<", "filename.txt") or die "Couldn't open file!";

ist idiomatisch. open gibt bei Erfolg etwas ungleich Null zurück (so dass der Teil die nach or nie passiert), aber bei einem Fehler undefiniert, und dann wird der Aufruf von die ausgeführt passieren (das ist Perls Weg, eine Ausnahme auszulösen).

Das ist gut in Sprachen, in denen jeder es tut.

10
RemcoGerlich

Obwohl ich im Allgemeinen mit Antwort von dan1111 einverstanden bin, gibt es einen besonders wichtigen Fall, der nicht behandelt wird: den Fall, in dem smartphone gleichzeitig verwendet wird. In diesem Szenario ist diese Art von Muster eine bekannte Quelle für schwer zu findende Fehler.

Das Problem ist, dass die Kurzschlussbewertung nicht atomar ist. Ihr Thread kann überprüfen, ob smartphonenull ist, ein anderer Thread kann hereinkommen und ihn aufheben, und dann versucht Ihr Thread sofort, GetSignal() und explodiert.

Aber natürlich nur, wenn sich die Sterne ausrichten und das Timing Ihrer Threads genau stimmt.

Obwohl diese Art von Code sehr verbreitet und nützlich ist, ist es wichtig, über diese Einschränkung Bescheid zu wissen, damit Sie diesen bösen Fehler genau verhindern können.

6
Telastyn

Es gibt viele Situationen, in denen ich zuerst eine Bedingung überprüfen und nur dann eine zweite Bedingung überprüfen möchte, wenn die erste Bedingung erfolgreich war. Manchmal nur aus Effizienzgründen (weil es keinen Sinn macht, die zweite Bedingung zu überprüfen, wenn die erste bereits fehlgeschlagen ist), manchmal, weil mein Programm sonst abstürzen würde (Ihre Prüfung zuerst auf NULL), manchmal, weil sonst die Ergebnisse schrecklich wären. (WENN (Ich möchte ein Dokument drucken) UND (Drucken eines Dokuments fehlgeschlagen)) DANN wird eine Fehlermeldung angezeigt - Sie möchten das Dokument nicht drucken und prüfen, ob der Druck fehlgeschlagen ist, wenn Sie kein Dokument in der Datei drucken möchten erster Platz!

Es bestand also ein RIESIGER Bedarf an einer garantierten Kurzschlussbewertung logischer Ausdrücke. Es war in Pascal (das keine garantierte Kurzschlussbewertung hatte) nicht vorhanden, was ein großer Schmerz war; Ich habe es zuerst in Ada und C gesehen.

Wenn Sie wirklich der Meinung sind, dass dies eine "schlechte Praxis" ist, nehmen Sie bitte ein mäßig komplexes Stück Code, schreiben Sie es neu, damit es ohne Kurzschlussbewertung einwandfrei funktioniert, und kommen Sie zurück und sagen Sie uns, wie es Ihnen gefallen hat. Dies ist so, als würde man sagen, dass das Atmen Kohlendioxid produziert und gefragt wird, ob das Atmen eine schlechte Praxis ist. Versuchen Sie es ein paar Minuten lang ohne.

3
gnasher729

Eine Überlegung, die in anderen Antworten nicht erwähnt wird: Manchmal können diese Überprüfungen auf eine mögliche Umgestaltung des Entwurfsmusters Null Object hinweisen. Zum Beispiel:

if (currentUser && currentUser.isAdministrator()) 
  doSomething();

Könnte vereinfacht werden, um nur zu sein:

if (currentUser.isAdministrator())
  doSomething ();

Wenn currentUser standardmäßig auf einen 'anonymen Benutzer' oder 'Nullbenutzer' mit einer Fallback-Implementierung eingestellt ist, wenn der Benutzer nicht angemeldet ist.

Nicht immer ein Codegeruch, sondern etwas zu beachten.

2
Pete