Ich programmiere jetzt seit ein paar Jahren und habe vor kurzem begonnen, ReSharper mehr zu verwenden. Eine Sache, die ReSharper mir immer vorschlägt, ist, die if-Anweisung zu invertieren, um die Verschachtelung zu reduzieren.
Angenommen, ich habe diesen Code:
foreach (someObject in someObjectList)
{
if(someObject != null)
{
someOtherObject = someObject.SomeProperty;
}
}
Und ReSharper schlägt vor, dass ich das mache:
foreach (someObject in someObjectList)
{
if(someObject == null) continue;
someOtherObject = someObject.SomeProperty;
}
Es scheint, dass ReSharper IMMER vorschlägt, dass ich IFs invertiere, egal wie viel verschachtelt wird. Dieses Problem ist, dass ich zumindest in EINIGEN Situationen gerne verschachtele. Für mich scheint es einfacher zu sein, zu lesen und herauszufinden, was an bestimmten Orten vor sich geht. Dies ist nicht immer der Fall, aber ich fühle mich manchmal wohler beim Nisten.
Meine Frage lautet: Gibt es außer persönlichen Vorlieben oder Lesbarkeit einen Grund, die Verschachtelung zu reduzieren? Gibt es einen Leistungsunterschied oder etwas anderes, das mir möglicherweise nicht bewusst ist?
quelle
Antworten:
Es hängt davon ab, ob. In Ihrem Beispiel
if
ist es kein Problem, die nicht invertierte Anweisung zu haben, da der Hauptteil vonif
sehr kurz ist. Normalerweise möchten Sie die Anweisungen jedoch umkehren, wenn der Körper wächst.Wir sind nur Menschen und es ist schwierig, mehrere Dinge gleichzeitig im Kopf zu behalten.
Wenn der Hauptteil einer Anweisung groß ist, verringert das Umkehren der Anweisung nicht nur die Verschachtelung, sondern teilt dem Entwickler auch mit, dass er alles über dieser Anweisung vergessen und sich nur auf den Rest konzentrieren kann. Es ist sehr wahrscheinlich, dass Sie die invertierten Anweisungen verwenden, um falsche Werte so schnell wie möglich zu entfernen. Sobald Sie wissen, dass diese nicht mehr im Spiel sind, bleiben nur noch Werte, die tatsächlich verarbeitet werden können.
quelle
break
, diecontinue
und mehrerereturn
nicht strukturiert sind.break
/continue
/return
einen echten Vorteil gegenüber einem habenelse
Block. Bei einem frühen Ausstieg gibt es zwei Blöcke : den Ausstiegsblock und den Aufenthaltsblock. mitelse
gibt es 3 Blöcke : if, else und was auch immer danach kommt (was auch immer der Zweig verwendet wird, der genommen wird). Ich bevorzuge weniger Blöcke.Vermeiden Sie nicht Negative. Menschen saugen als sie zu analysieren, auch wenn die CPU sie liebt.
Respektieren Sie jedoch Redewendungen. Wir Menschen sind Gewohnheitstiere, daher bevorzugen wir abgenutzte Pfade gegenüber direkten Pfaden voller Unkräuter.
foo != null
ist leider zu einer Redewendung geworden. Die Einzelteile fallen mir kaum noch auf. Ich schaue mir das an und denke nur: "Oh, es ist sicher, dass wirfoo
jetzt abhauen können."Wenn du das umstürzen willst (oh, eines Tages, bitte), brauchst du einen wirklich starken Fall. Sie brauchen etwas, mit dem meine Augen mühelos davon abrutschen und es sofort verstehen.
Sie gaben uns jedoch Folgendes:
Welches ist nicht einmal strukturell gleich!
Das ist nicht das, was mein fauler Kopf erwartet hatte. Ich dachte, ich könnte weiterhin unabhängigen Code hinzufügen, aber ich kann nicht. Das bringt mich dazu, über Dinge nachzudenken, an die ich jetzt nicht denken möchte. Sie haben meine Redewendung gebrochen, mir aber keine bessere gegeben. Alles nur, weil du mich vor dem einen Negativ beschützen wolltest, das mir ein böses kleines Selbst in die Seele gebrannt hat.
Tut mir leid, vielleicht schlägt ReSharper dies zu 90% der Zeit vor, aber ich denke, Sie haben bei Nullprüfungen einen Eckfall gefunden, in dem es am besten ignoriert wird. Tools können Ihnen einfach nicht beibringen, was lesbarer Code ist. Frage einen Menschen. Führen Sie eine Begutachtung durch. Aber folge dem Werkzeug nicht blindlings.
quelle
if (foo != null){ foo.something() }
ich weiterhin Code hinzufügen, ohne mich darum zu kümmern, obfoo
null war. Das tut es nicht. Auch wenn ich das jetzt nicht tun muss, fühle ich mich nicht motiviert, die Zukunft zu vermasseln, indem ich sage: "Nun, sie können es einfach umgestalten, wenn sie das brauchen." Feh. Software ist nur dann weich, wenn sie leicht zu ändern ist.Es ist das alte Argument, ob eine Pause schlimmer ist als ein Nest.
Die Leute scheinen eine frühzeitige Rückgabe für Parameterprüfungen zu akzeptieren, aber die meisten Methoden sind verpönt.
Persönlich vermeide ich, fortzufahren, zu brechen, usw. zu gehen, auch wenn es mehr Verschachtelung bedeutet. In den meisten Fällen können Sie jedoch beides vermeiden.
Offensichtlich erschwert das Verschachteln die Verfolgung, wenn Sie viele Ebenen haben
Das Schlimmste ist, wenn Sie beide haben
quelle
foreach (subObject in someObjectList.where(i=>i != null))
Weiß nicht, warum ich noch nie so etwas gedacht habe.Das Problem dabei
continue
ist, dass die Logik kompliziert wird und wahrscheinlich Fehler enthält, wenn Sie dem Code weitere Zeilen hinzufügen. z.BMöchten Sie
doSomeOtherFunction()
für alle someObject aufrufen oder nur, wenn nicht null? Mit dem Originalcode haben Sie die Möglichkeit und es wird klarer. Mit demcontinue
könnte man vergessen "oh, yeah, da hinten haben wir Nullen übersprungen" und Sie haben nicht einmal die Möglichkeit, es für alle someObjects aufzurufen, es sei denn, Sie setzen diesen Aufruf am Anfang der Methode, was möglicherweise nicht möglich oder korrekt ist aus anderen gründen.Ja, viel Schachteln ist hässlich und möglicherweise verwirrend, aber in diesem Fall würde ich den traditionellen Stil verwenden.
Bonusfrage : Warum enthält diese Liste zunächst Nullen? Es ist möglicherweise besser, überhaupt keine Null hinzuzufügen. In diesem Fall ist die Verarbeitungslogik viel einfacher.
quelle
return
weil sie eine "saubere Pause" bilden, aber IMObreak
undcontinue
zu Verwirrung führen und in den meisten Fällen am besten vermieden werden.Die Idee ist die frühe Rückkehr. Früh
return
in einer Schleife wird frühcontinue
.Viele gute Antworten auf diese Frage: Soll ich frühzeitig von einer Funktion zurückkehren oder eine if-Anweisung verwenden?
Beachten Sie, dass 430+ Stimmen für eine vorzeitige Rückkehr sind, ~ 50 Stimmen für eine vorzeitige Rückkehr und 8 gegen eine vorzeitige Rückkehr. Es besteht also ein außergewöhnlich starker Konsens (entweder das, oder ich zähle schlecht, was plausibel ist).
Persönlich neige ich dazu, den Schleifenkörper in eine Funktion zu extrahieren, in der ich Early Return anstelle von Early Continue verwende.
quelle
Diese Technik ist nützlich, um Vorbedingungen zu zertifizieren, wenn der Hauptteil Ihrer Methode auftritt, wenn diese Bedingungen erfüllt sind.
ifs
Bei meiner Arbeit kehren wir häufig um und setzen ein Phantom ein. Früher war es ziemlich häufig, dass ich beim Überprüfen einer PR darauf hinweisen konnte, wie mein Kollege drei Verschachtelungsebenen entfernen konnte! Es kommt seltener vor, da wir unser Wenn ausrollen.Hier ist ein Spielzeugbeispiel für etwas, das ausgerollt werden kann
Code wie dieser, in dem es EINEN interessanten Fall gibt (in dem der Rest einfach Rückgaben oder kleine Anweisungsblöcke sind), ist weit verbreitet. Es ist trivial, das oben Gesagte als umzuschreiben
Hier wird unser signifikanter Code, die nicht eingeschlossenen 10 Zeilen, in der Funktion nicht dreifach eingerückt. Wenn Sie den ersten Stil haben, sind Sie entweder versucht, den langen Block in eine Methode umzugestalten, oder Sie tolerieren das Einrücken. Hier setzt die Einsparung ein. An einer Stelle ist dies eine triviale Einsparung, aber bei vielen Methoden in vielen Klassen sparen Sie sich die Notwendigkeit, eine zusätzliche Funktion zu generieren, um den Code sauberer zu machen, und Sie haben nicht so viel abscheuliche Mehrebenen Einrückung .
Als Reaktion auf OPs Codebeispiel stimme ich zu, dass dies ein übermäßiger Zusammenbruch ist. Besonders, da in 1 TB, dem von mir verwendeten Stil, die Inversion dazu führt, dass der Code größer wird.
quelle
Resharper-Vorschläge sind oft nützlich, sollten aber immer kritisch bewertet werden. Es sucht nach vordefinierten Codemustern und schlägt Transformationen vor, kann jedoch nicht alle Faktoren berücksichtigen, die den Code für den Menschen mehr oder weniger lesbar machen.
Wenn alle anderen Dinge gleich sind, ist eine geringere Verschachtelung vorzuziehen, weshalb ReSharper eine Transformation vorschlägt, die die Verschachtelung verringert. Aber auch alle anderen Dinge sind nicht gleich: In diesem Fall wird die Transformation die Einführung eines erfordert
continue
, was bedeutet , der resultierende Code ist tatsächlich schwieriger zu folgen.In einigen Fällen ein Niveaus der Verschachtelung für einen Austausch
continue
könnte in der Tat eine Verbesserung sein, aber das hängt von der Semantik des Codes in Frage, die über das Verständnis von ReSharpers mechanischen Regeln.In Ihrem Fall würde der ReSharper-Vorschlag den Code verschlimmern. Also ignoriere es einfach. Oder besser: Verwenden Sie nicht die vorgeschlagene Transformation, sondern überlegen Sie, wie der Code verbessert werden könnte. Beachten Sie, dass Sie möglicherweise dieselbe Variable mehrmals zuweisen, aber nur die letzte Zuweisung wirkt sich aus, da die vorherige nur überschrieben wird. Das sieht nach einem Bug aus. Der Vorschlag von ReSharpers behebt den Fehler nicht, macht jedoch deutlich, dass eine zweifelhafte Logik im Gange ist.
quelle
Ich denke, der größere Punkt hier ist, dass der Zweck der Schleife den besten Weg vorgibt, den Fluss innerhalb der Schleife zu organisieren. Wenn Sie einige Elemente herausfiltern, bevor Sie die verbleibenden Elemente durchlaufen,
continue
ist es sinnvoll, sie vorzeitig zu entfernen. Wenn Sie jedoch verschiedene Arten der Verarbeitung in einer Schleife ausführen, ist es möglicherweise sinnvoller, diesif
wirklich deutlich zu machen , z. B .:Beachten Sie, dass Sie in Java Streams oder anderen funktionalen Idiomen die Filterung von der Schleife trennen können, indem Sie Folgendes verwenden:
Übrigens ist dies eines der Dinge, die ich an Perls invertiertem if (
unless
) liebe , das auf einzelne Anweisungen angewendet wird und das die folgende Art von Code ermöglicht, die ich sehr einfach zu lesen finde:quelle