¿Por qué declarar variables cerca de donde se usan?

10

He escuchado a personas decir que las variables deben declararse lo más cerca posible de su uso. No entiendo esto

Por ejemplo, esta política sugeriría que debería hacer esto:

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

Pero seguramente esto significa que los gastos generales de la creación de una nueva intse incurre en cada iteración. ¿No sería mejor usar:

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

Por favor alguien podría explicar?

James
fuente
3
Sería un lenguaje bastante pobre que trataría esos dos casos de manera diferente.
Paul Tomblin
55
Usted a partir de una premisa falsa. Consulte mi respuesta a esta pregunta: stackoverflow.com/questions/6919655/…
CesarGon
8
Si piensas así, no eres apto para optimizar o incluso considerar los impactos en el rendimiento en general. Las implementaciones de lenguaje son inteligentes, y si crees que no lo son, demuéstralo con datos duros obtenidos a través de puntos de referencia imparciales y realistas.
44
Si los dos ejemplos de código tienen una diferencia semántica significativa, entonces hacen cosas diferentes. Debes usar el que hace lo que quieres hacer. La regla sobre dónde declarar variables solo se aplica a casos en los que no hay diferencia semántica.
David Schwartz
44
Considere el extremo opuesto de la escala: todo es una variable global. ¿Seguramente el "uso cercano declarado" es el mejor final de este espectro?
JBRWilkinson

Respuestas:

27

Esta es una regla de estilo entre muchas, y no es necesariamente la regla más importante de todas las reglas posibles que podrías considerar. Su ejemplo, dado que incluye un int, no es súper convincente, pero ciertamente podría tener un objeto costoso de construir dentro de ese ciclo, y quizás un buen argumento para construir el objeto fuera del ciclo. Sin embargo, eso no lo convierte en un buen argumento en contra de esta regla, ya que primero, hay toneladas de otros lugares que podría aplicar que no implican la construcción de objetos caros en un bucle, y segundo, un buen optimizador (y ha etiquetado C #, para que tenga un buen optimizador) puede izar la inicialización fuera del ciclo.

La verdadera razón de esta regla también es la razón por la que no ves por qué es una regla. La gente solía escribir funciones que tenían cientos, incluso miles de líneas de largo y solía escribirlas en editores de texto sin formato (piense en el Bloc de notas) sin el tipo de soporte que proporciona Visual Studio. En ese entorno, declarar una variable a cientos de líneas de donde se usaba significaba que la persona que leía

if (flag) limit += factor;

no tenía muchas pistas sobre qué bandera, límite y factor eran. Se adoptaron convenciones de nomenclatura como la notación húngara para ayudar con esto, y también reglas como declarar cosas cercanas a donde se usan. Por supuesto, en estos días, todo se trata de refactorizar, y las funciones generalmente duran menos de una página, lo que dificulta la distancia entre el lugar donde se declaran las cosas y donde se usan. Estás operando en un rango de 0-20 y estás discutiendo que tal vez 7 está bien en este caso en particular, mientras que el tipo que hizo la regla habría AMADO tener 7 líneas de distancia y estaba tratando de convencer a alguien de 700. Y en Además de eso, en Visual Studio, puede pasar el mouse sobre cualquier cosa y ver su tipo, si es una variable miembro, etc. Eso significa que la necesidad de ver la línea que lo declara disminuye.

Sigue siendo una regla razonablemente buena, una que en realidad es bastante difícil de romper en estos días, y una que nadie abogó como razón para escribir código lento. Sé sensible, sobre todo.

Kate Gregory
fuente
Gracias por tu respuesta. Pero seguramente, independientemente del tipo de datos, ¿se crea una nueva instancia en cada iteración de cualquier forma que lo haga? Es solo que en el segundo caso no pedimos una nueva referencia de memoria cada vez. ¿O me he perdido el punto? ¿Y está diciendo que el optimizador de C # mejorará automáticamente mi código cuando se compile de todos modos? Yo no lo sabia!
James
2
La sobrecarga de crear un int es pequeña. Si estuvieras construyendo algo complicado, la sobrecarga sería un problema mayor.
Kate Gregory
17
No se trata solo de poder ver su tipo y tal. También es una cuestión de por vida. Si la variable "wibble" se declara 30 líneas antes de que se use por primera vez, hay 30 líneas en las que un uso erróneo de "wibble" puede provocar un error. Si se declara inmediatamente antes de usarse, el uso de "wibble" en esas 30 líneas anteriores no generará un error. Causará un error de compilación en su lugar.
Mike Sherrill 'Cat Recall'
En este caso, no se crea una nueva instancia en cada bucle. Se crea una única variable de nivel superior y se usa para cada iteración (observe la IL). Pero ese es un detalle de implementación.
thecoop
"en Visual Studio, puede pasar el mouse sobre cualquier cosa y ver", etc. También existe Navegar a la definición, que tiene el acceso directo F12que es indispensable.
StuperUser
15

La definición de la variable dentro del bucle hace que la visibilidad sea local solo para ese bucle. Esto tiene al menos 3 ventajas para el lector:

  1. La definición variable y cualquier comentario relacionado son fáciles de encontrar
  2. El lector sabe que esta variable nunca se usa en otro lugar donde (no hay dependencia que esperar)
  3. Cuando el código se escribe o edita, no hay posibilidad de que pueda usar el mismo nombre de variable fuera del bucle para referirse a esa variable, de lo contrario, podría obtener un error.

En cuanto al bit de eficiencia, el compilador es inteligente para generar la definición fuera del bucle en el código optimizado generado. La variable no se creará en cada iteración del bucle.

Ninguna posibilidad
fuente
4

Las personas dicen que lo más cerca posible de su uso , no dicen que debas hacer eso todo el tiempo, porque en algunos casos, declarar que las variables en el menor alcance causarán cierta sobrecarga. Las principales razones de esa declaración son la legibilidad y las variables El alcance más pequeño que puedas.

invariante
fuente
4

Aunque ayuda con la legibilidad, la legibilidad no es la consideración principal en este caso, y los IDE modernos no obvian la necesidad de esta regla.

La principal preocupación son las variables no inicializadas. Si declaras una variable demasiado lejos de su inicialización, te abre a todo tipo de problemas potenciales. Es posible que te encuentres trabajando accidentalmente con lo que sea que haya estado allí antes en la RAM, o el resultado de un cálculo más alto en la función, o una inicialización ficticia (como 0) que alguien puso solo para evitar que el compilador se queje. Las personas insertarán código entre su declaración y uso sin ser conscientes de sus condiciones previas implícitas para esa variable. En el peor de los casos, ese uso funcionará en tus pruebas pero fallará en el campo.

Declarar sus variables en el menor alcance posible e inicializarlas en un valor adecuado justo en el punto de declaración evitará muchos dolores de cabeza de mantenimiento. El hecho de que obliga a mejorar la legibilidad es solo un buen efecto secundario.

Karl Bielefeldt
fuente
1

No es un "must". Es solo una opinión, es una manera de hacer algo. Por ejemplo, me gusta declarar todos los vars en las primeras líneas del método para poder comentar qué haré con esos vars (por supuesto, a menos que sean contadores). A otras personas, como escucharon, les gusta colocarlas lo más cerca posible de su uso (como en el segundo ejemplo que escribió). De todos modos, el primer ejemplo que proporcione es seguramente un "error" (en el sentido de que causará una sobrecarga como usted lo entiende).

Simplemente tiene que elegir su camino y seguirlo.

Aurelio De Rosa
fuente
2
No es solo una opinión, ¿verdad? ¿La investigación en ingeniería de software no ha documentado la relación entre el tiempo en vivo y la cantidad de errores desde al menos la década de 1980?
Mike Sherrill 'Cat Recall'
1

Sus dos ejemplos son códigos funcionalmente diferentes, no son intercambiables. (Sus ejemplos simplificados le dejan una distinción sin diferencia, pero en un código no trivial hace la diferencia). La regla de su sitio siempre está subordinada a consideraciones de alcance, como se indica con "... como sea posible".

kylben
fuente