Warum Variablen in der Nähe des Verwendungsortes deklarieren?

10

Ich habe Leute sagen hören , dass Variablen so nah wie möglich an ihrer Verwendung deklariert werden sollten. Ich verstehe das nicht

Zum Beispiel würde diese Richtlinie vorschlagen, dass ich dies tun sollte:

foreach (var item in veryLongList) {
  int whereShouldIBeDeclared = item.Id;
  //...
}

Dies bedeutet jedoch sicherlich int, dass bei jeder Iteration der Aufwand für die Erstellung einer neuen Datei anfällt. Wäre es nicht besser zu verwenden:

int whereShouldIBeDeclared;
foreach (var item in veryLongList) {
  whereShouldIBeDeclared = item.Id;
  //...
}

Könnte mir bitte jemand erklären?

James
quelle
3
Es wäre eine verdammt schlechte Sprache, die diese beiden Fälle unterschiedlich behandelte.
Paul Tomblin
5
Sie gehen von einer falschen Prämisse aus. Bitte sehen Sie meine Antwort auf diese Frage: stackoverflow.com/questions/6919655/…
CesarGon
8
Wenn Sie so denken, sind Sie nicht in der Lage, die Auswirkungen auf die Leistung im Allgemeinen zu optimieren oder gar zu berücksichtigen. Sprachimplementierungen sind intelligent, und wenn Sie der Meinung sind, dass dies nicht der Fall ist, beweisen Sie dies anhand von harten Daten, die durch unvoreingenommene, realistische Benchmarks erhalten wurden.
4
Wenn die beiden Codebeispiele einen signifikanten semantischen Unterschied aufweisen, tun sie unterschiedliche Dinge. Sie sollten die verwenden, die das tut, was Sie tun möchten. Die Regel, wo Sie Variablen deklarieren, gilt nur für Fälle, in denen es keinen semantischen Unterschied macht.
David Schwartz
4
Betrachten Sie das entgegengesetzte Ende der Skala - alles ist eine globale Variable. Sicherlich ist "deklariert in der Nähe der Nutzung" das bessere Ende dieses Spektrums?
JBRWilkinson

Antworten:

27

Dies ist eine Stilregel unter vielen, und es ist nicht unbedingt die wichtigste Regel aller möglichen Regeln, die Sie in Betracht ziehen könnten. Ihr Beispiel, da es ein int enthält, ist nicht besonders überzeugend, aber Sie könnten sicherlich ein teuer zu konstruierendes Objekt in dieser Schleife haben und vielleicht ein gutes Argument für die Konstruktion des Objekts außerhalb der Schleife. Dies ist jedoch kein gutes Argument gegen diese Regel, da erstens unzählige andere Stellen angewendet werden können, an denen keine teuren Objekte in einer Schleife erstellt werden müssen, und zweitens ein guter Optimierer (und Sie haben sie markiert) C #, damit Sie einen guten Optimierer haben) kann die Initialisierung aus der Schleife heben.

Der wahre Grund für diese Regel ist auch der Grund, warum Sie nicht verstehen, warum es sich um eine Regel handelt. Früher schrieben die Leute Funktionen, die Hunderte oder sogar Tausende von Zeilen lang waren, und sie schrieben sie in Nur-Text-Editoren (Think Notepad) ohne die Unterstützung, die Visual Studio bot. In dieser Umgebung bedeutete das Deklarieren einer Variablen, die Hunderte von Zeilen von ihrem Verwendungsort entfernt war, dass die Person las

if (flag) limit += factor;

Ich hatte nicht viele Hinweise darauf, was Flagge, Limit und Faktor waren. Namenskonventionen wie die ungarische Notation wurden eingeführt, um dies zu unterstützen, ebenso wie Regeln wie die Erklärung von Dingen in der Nähe ihres Verwendungsortes. Natürlich dreht sich heutzutage alles um Refactoring, und Funktionen sind im Allgemeinen weniger als eine Seite lang, was es schwierig macht, einen großen Abstand zwischen dem Ort, an dem Dinge deklariert werden, und dem Ort, an dem sie verwendet werden, zu finden. Sie arbeiten in einem Bereich von 0 bis 20 und streiten darüber, dass in diesem speziellen Fall vielleicht 7 in Ordnung ist, während der Typ, der die Regel aufgestellt hat, es geliebt hätte, 7 Zeilen wegzubekommen und versucht hätte, jemanden von 700 herunterzureden. Und weiter Darüber hinaus können Sie in Visual Studio mit der Maus über alles fahren und dessen Typ anzeigen, ist es eine Mitgliedsvariable und so weiter. Das bedeutet, dass die Notwendigkeit, die Linie zu sehen, die sie deklariert, verringert wird.

Es ist immer noch eine ziemlich gute Regel, eine, die heutzutage ziemlich schwer zu brechen ist und die niemand jemals als Grund für das Schreiben von langsamem Code befürwortet hat. Sei vor allem vernünftig.

Kate Gregory
quelle
Danke für deine Antwort. Aber sicherlich wird unabhängig vom Datentyp bei jeder Iteration eine neue Instanz erstellt, wie auch immer ich es mache? Es ist nur so, dass wir im zweiten Fall nicht jedes Mal nach einer neuen Speicherreferenz fragen. Oder habe ich den Punkt verpasst? Und sagen Sie, dass der C # -Optimierer meinen Code automatisch verbessert, wenn er trotzdem kompiliert wird? Das wusste ich nicht!
James
2
Der Aufwand für die Erstellung eines Int ist gering. Wenn Sie etwas Kompliziertes konstruieren würden, wäre der Overhead eine größere Sache.
Kate Gregory
17
Es geht nicht nur darum, seinen Typ und so zu sehen. Es ist auch eine Frage des Lebens. Wenn die Variable "wibble" 30 Zeilen deklariert wird, bevor sie zum ersten Mal verwendet wird, gibt es 30 Zeilen, in denen eine fehlerhafte Verwendung von "wibble" zu einem Fehler führen kann. Wenn es unmittelbar vor seiner Verwendung deklariert wird, führt die Verwendung von "wibble" in diesen 30 vorherigen Zeilen nicht zu einem Fehler. Es wird stattdessen einen Compilerfehler verursachen.
Mike Sherrill 'Cat Recall'
In diesem Fall wird nicht in jeder Schleife eine neue Instanz erstellt. Für jede Iteration wird eine einzelne Variable der obersten Ebene erstellt und verwendet (siehe IL). Aber das ist ein Implementierungsdetail.
Thecoop
"In Visual Studio können Sie mit der Maus über alles fahren und sehen" usw. Es gibt auch Navigieren zur Definition, deren Verknüpfung F12unverzichtbar ist.
StuperUser
15

Durch das Definieren der Variablen innerhalb der Schleife wird sie nur für diese Schleife lokal sichtbar. Dies hat mindestens 3 Vorteile für den Leser:

  1. Die Variablendefinition und alle zugehörigen Kommentare sind leicht zu finden
  2. Der Leser weiß, dass diese Variable nirgendwo anders verwendet wird (keine Abhängigkeit zu erwarten)
  3. Wenn der Code geschrieben oder bearbeitet wird, besteht keine Möglichkeit, dass Sie denselben Variablennamen außerhalb der Schleife verwenden, um auf diese Variable zu verweisen. Andernfalls kann es zu Fehlern kommen.

Was das Effizienzbit betrifft, ist der Compiler klug, die Definition außerhalb der Schleife in dem generierten optimierten Code zu generieren. Die Variable wird nicht bei jeder Schleifeniteration erstellt.

Keine Chance
quelle
4

Die Leute sagen, dass sie so nah wie möglich an ihrer Verwendung sind. Sie sagen nicht, dass Sie dies die ganze Zeit tun müssen, da in einigen Fällen die Angabe von Variablen im geringsten Umfang einen gewissen Overhead verursacht. Die Hauptgründe für diese Anweisung sind Lesbarkeit und Angabe von Variablen der kleinste Umfang, den Sie können.

invariant
quelle
4

Obwohl dies die Lesbarkeit verbessert, steht die Lesbarkeit in diesem Fall nicht im Vordergrund, und moderne IDEs machen diese Regel nicht überflüssig.

Das Hauptanliegen sind nicht initialisierte Variablen. Wenn Sie eine Variable zu weit von ihrer Initialisierung entfernt deklarieren, sind Sie für alle möglichen Probleme offen. Möglicherweise arbeiten Sie versehentlich mit dem, was sich zuvor im RAM befand, oder mit dem Ergebnis einer höheren Berechnung in der Funktion oder einer Dummy-Initialisierung (wie 0), die jemand eingegeben hat, um den Compiler davon abzuhalten, sich zu beschweren. Die Benutzer fügen Code zwischen Ihrer Deklaration und Verwendung ein, ohne Ihre impliziten Voraussetzungen für diese Variable zu kennen. Im schlimmsten Fall funktioniert diese Verwendung nur in Ihren Tests, schlägt jedoch vor Ort fehl.

Wenn Sie Ihre Variablen in einem möglichst kleinen Bereich deklarieren und sie direkt zum Zeitpunkt der Deklaration auf einen geeigneten Wert initialisieren, werden viele Wartungsprobleme vermieden. Die Tatsache, dass es eine verbesserte Lesbarkeit erzwingt, ist nur ein netter Nebeneffekt.

Karl Bielefeldt
quelle
1

Es ist kein "Muss". Es ist nur eine Meinung, ich mache etwas. Zum Beispiel möchte ich alle Variablen in den ersten Zeilen der Methode deklarieren, damit ich kommentieren kann, was ich mit diesen Variablen machen werde (natürlich, es sei denn, sie sind Zähler). Wie Sie gehört haben, möchten andere Personen sie so nah wie möglich an ihrer Verwendung platzieren (wie im zweiten Beispiel, das Sie geschrieben haben). Wie auch immer, das erste Beispiel, das Sie angeben, ist sicherlich ein "Fehler" (in dem Sinne, dass es, wie Sie verstehen, einen Overhead verursacht).

Sie müssen einfach Ihren Weg wählen und ihm folgen.

Aurelio De Rosa
quelle
2
Es ist nicht nur eine Meinung, oder? Hat die Softwareentwicklung nicht seit mindestens den 1980er Jahren die Beziehung zwischen der Live-Zeit und der Anzahl der Fehler dokumentiert?
Mike Sherrill 'Cat Recall'
1

Ihre beiden Beispiele sind funktional unterschiedlicher Code, sie sind nicht austauschbar. (Ihre abgespeckten Beispiele lassen eine Unterscheidung ohne Unterschied, aber in nicht trivialem Code macht es einen Unterschied). Die Regel, die Sie auf Ihrer Website veröffentlichen, unterliegt immer den Überlegungen zum Geltungsbereich, wie durch "... wie möglich" angegeben.

kylben
quelle