Perché dichiarare le variabili vicine a dove vengono utilizzate?


10

Ho sentito la gente dire che le variabili dovrebbero essere dichiarate il più vicino possibile al loro utilizzo. Non lo capisco

Ad esempio, questa politica suggerisce che dovrei fare questo:

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

Ma sicuramente questo significa che le spese generali di creazione di un nuovo intsono sostenute in ogni iterazione. Non sarebbe meglio usare:

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

Per favore qualcuno potrebbe spiegare?


3
Sarebbe un linguaggio maledettamente povero che trattasse questi due casi in modo diverso.
Paul Tomblin,

5
A partire da una premessa errata. Si prega di vedere la mia risposta a questa domanda: stackoverflow.com/questions/6919655/...
CesarGon

8
Se la pensi così, non sei in grado di ottimizzare o addirittura considerare gli impatti sulle prestazioni in generale. Le implementazioni linguistiche sono intelligenti e, se pensi che non lo siano, dimostralo con dati concreti ottenuti attraverso benchmark imparziali e realistici.

4
Se i due esempi di codice hanno una differenza semantica significativa, allora fanno cose diverse. Dovresti usare quello che fa quello che vuoi fare. La regola su dove si dichiarano le variabili si applica solo ai casi in cui non fa alcuna differenza semantica.
David Schwartz,

4
Considera l'estremità opposta della scala: tutto è una variabile globale. Sicuramente "dichiarato vicino all'uso" è la fine migliore di questo spettro?
JBR Wilkinson,

Risposte:


27

Questa è una regola di stile tra molte e non è necessariamente la regola più importante di tutte le possibili regole che potresti prendere in considerazione. Il tuo esempio, dal momento che include un int, non è super convincente, ma potresti sicuramente avere un oggetto costoso da costruire all'interno di quel ciclo e forse un buon argomento per costruire l'oggetto al di fuori del ciclo. Tuttavia, ciò non lo rende un buon argomento contro questa regola sin dall'inizio, ci sono tonnellate di altri posti che potrebbero applicare che non comportano la costruzione di oggetti costosi in un ciclo e, in secondo luogo, un buon ottimizzatore (e hai taggato C #, quindi hai un buon ottimizzatore) può sollevare l'inizializzazione fuori dal ciclo.

Il vero motivo di questa regola è anche il motivo per cui non vedi perché è una regola. Le persone scrivevano funzioni lunghe centinaia, persino migliaia di righe e le scrivevano in editor di testo semplice (pensa Notepad) senza il tipo di supporto fornito da Visual Studio. In quell'ambiente, dichiarare una variabile a centinaia di righe di distanza da dove veniva usata significava che la persona leggeva

if (flag) limit += factor;

non aveva molti indizi su cosa fossero bandiera, limite e fattore. Convenzioni di denominazione come la notazione ungherese sono state adottate per aiutare in questo, così come regole come dichiarare cose vicine a dove vengono utilizzate. Ovviamente, in questi giorni, si tratta di refactoring e le funzioni sono generalmente lunghe meno di una pagina, il che rende difficile ottenere molta distanza tra dove vengono dichiarate le cose e dove vengono utilizzate. Stai operando in un intervallo compreso tra 0 e 20 e stai scherzando sul fatto che forse 7 è ok in questo caso particolare, mentre il ragazzo che ha fatto la regola avrebbe ADORATO ottenere 7 righe di distanza e stava cercando di parlare di qualcuno da 700. E poi Inoltre, in Visual Studio, puoi passare con il mouse su qualsiasi cosa e vedere il suo tipo, è una variabile membro e così via. Ciò significa che la necessità di vedere la riga che la dichiara è ridotta.

È ancora una regola ragionevolmente buona, una che in realtà è abbastanza difficile da infrangere in questi giorni e una che nessuno ha mai sostenuto come motivo per scrivere codice lento. Sii sensibile, soprattutto.


Grazie per la tua risposta. Ma sicuramente indipendentemente dal tipo di dati, viene creata una nuova istanza su ogni iterazione in qualunque modo lo faccia? È solo che nel secondo caso non chiediamo ogni volta un nuovo riferimento di memoria. O ho perso il punto? E stai dicendo che l'ottimizzatore C # migliorerà automaticamente il mio codice quando si compila comunque? Non lo sapevo!
James,

2
Il sovraccarico di creare un int è minuscolo. Se stavi costruendo qualcosa di complicato, il sovraccarico sarebbe un grosso problema.
Kate Gregory,

17
Non è solo una questione di poter vedere il suo tipo e simili. È anche una questione di vita. Se la variabile "wibble" viene dichiarata 30 righe prima del suo primo utilizzo, ci sono 30 righe in cui un uso errato di "wibble" può provocare un errore. Se viene dichiarato immediatamente prima dell'uso, l'utilizzo di "wibble" in quelle 30 righe precedenti non comporterà un bug. Invece causerà un errore del compilatore.
Mike Sherrill "Cat Recall",

In questo caso, una nuova istanza non viene creata in ogni ciclo. Una singola variabile di livello superiore viene creata e utilizzata per ogni iterazione (guarda IL). Ma questo è un dettaglio di implementazione.
thecoop

"in Visual Studio, puoi passare con il mouse su qualsiasi cosa e vedere" ecc. Esiste anche Navigazione verso la definizione, che ha il collegamento F12indispensabile.
StuperUser

15

La definizione della variabile all'interno del loop rende la visibilità locale solo per quel loop. Questo ha almeno 3 vantaggi per il lettore:

  1. La definizione della variabile e eventuali commenti correlati sono facili da trovare
  2. Il lettore sa che questa variabile non viene mai utilizzata altrove (nessuna dipendenza da aspettarsi)
  3. Quando il codice viene scritto o modificato, non è possibile utilizzare lo stesso nome di variabile al di fuori del ciclo per fare riferimento a tale variabile, altrimenti è possibile che si verifichino errori.

Per quanto riguarda il bit di efficienza, il compilatore è intelligente per generare la definizione al di fuori del ciclo nel codice ottimizzato generato. La variabile non verrà creata ad ogni iterazione di loop.


4

Le persone dicono il più vicino possibile al loro utilizzo , non stanno dicendo che dovresti farlo tutto il tempo, perché sono alcuni casi che dichiarano variabili nel minimo ambito causeranno un sovraccarico. I motivi principali di tale affermazione sono leggibilità e dare variabili il più piccolo ambito possibile.


4

Anche se aiuta con la leggibilità, la leggibilità non è la considerazione principale in questo caso e gli IDE moderni non ovviano alla necessità di questa regola.

La preoccupazione principale sono le variabili non inizializzate. Se dichiari una variabile troppo lontana dalla sua inizializzazione, ti apre a tutti i tipi di potenziali problemi. Potresti trovarti a lavorare accidentalmente con qualsiasi cosa sia successa nella RAM in precedenza, o il risultato di un calcolo superiore nella funzione o un'inizializzazione fittizia (come 0) che qualcuno ha inserito solo per evitare che il compilatore si lamentasse. Le persone inseriranno il codice tra la dichiarazione e l'utilizzo senza essere consapevoli dei presupposti impliciti per quella variabile. Nel peggiore dei casi, quell'utilizzo funzionerà nei test ma fallirà sul campo.

Dichiarare le variabili in un ambito il più piccolo possibile e inizializzarle su un valore adeguato nel punto della dichiarazione eviterà un gran numero di mal di testa da manutenzione. Il fatto che imponga una migliore leggibilità è solo un piacevole effetto collaterale.


1

Non è un "must". È solo un'opinione, ho modo di fare qualcosa. Ad esempio, mi piace dichiarare tutte le variabili nelle prime righe del metodo in modo da poter commentare cosa farò con quelle variabili (ovviamente a meno che non siano contatori). Ad altre persone, come hai sentito, piace metterle il più vicino possibile al loro utilizzo (come nel secondo esempio che hai scritto). Ad ogni modo, il primo esempio che fornisci è sicuramente un "errore" (nel senso che causerà un sovraccarico, come capisci).

Devi semplicemente scegliere la tua strada e seguirla.


2
Non è solo un'opinione, vero? La ricerca di ingegneria del software non ha documentato la relazione tra il tempo reale e il numero di bug almeno dagli anni '80?
Mike Sherrill "Cat Recall",

1

I tuoi due esempi sono codici funzionalmente diversi, non sono intercambiabili. (I tuoi esempi eliminati lasciano una distinzione senza differenza, ma in un codice non banale fa la differenza). La regola del sito è sempre subordinata a considerazioni di ambito, come indicato da "... il più possibile".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.