¿Por qué las variables no utilizadas son un problema?

9

Estaba limpiando advertencias de variables no utilizadas un día, y comencé a reflexionar, ¿cuál es exactamente el problema con ellas?

De hecho, algunos de ellos incluso ayudan en la depuración (por ejemplo, inspeccionar detalles de excepción o verificar el valor de retorno antes de devolver).

No pude encontrar un riesgo real real al tenerlos ...

Ejemplos

No me refiero a líneas que toman tiempo de la atención de otros programadores, tales como:

int abc = 7;

Esa es una obvia redundancia y distracción. Me refiero a cosas como:

try {
    SomeMethod();
} catch (SomeException e) {
    // here e is unused, but during debug we can inspect exception details
    DoSomethingAboutIt();
}
Alquitrán
fuente
31
¿Quieres decir que no es el hecho de que el programador que viene después de ti tendrá que pasar tiempo tratando de descubrir por qué están allí?
Robert Harvey
1
Sus ejemplos son inofensivos, pero nunca he visto casos como los denominados "variables no utilizadas". ¿De dónde vienen tus advertencias?
Jacob Raihle
99
Bueno, retno está sin usar.
Robert Harvey
44
Escribiste " Me refiero a cosas como catch (SomeException e) ", estás fingiendo que hay otros casos similares. Ilumíname, ¿cuáles? Pregunto porque en C # o C ++, tengo problemas para encontrar una situación diferente para su caso.
Doc Brown

Respuestas:

4

Según el marco que ha proporcionado, es difícil argumentar en contra de esas variables per se:

try {
    SomeMethod();
} catch (SomeException e) {
// here e is unused, but during debug we can inspect exception details
DoSomethingAboutIt();
}

Toma esto por ejemplo. Está detectando una excepción y (quizás) tiene un código que trata la excepción de manera adecuada. El hecho de que no esté utilizando la instancia real de la excepción no hace daño, ni al flujo del código ni a la comprensibilidad.

Pero lo que me hace pensar es cuando escribes sobre la legitimación de »variables no utilizadas«

De hecho, algunos de ellos incluso ayudan a depurar

Ese no es el propósito de escribir código: tener que depurarlo más tarde. Para evitar malentendidos, debo decir que soy muy consciente de que muchas veces tienes que arreglar tu código; y a veces la depuración es la solución. Pero no debería ser lo primero, lo que se te ocurra, al escribir el código, que dejes "puntos de anclaje", cuyo único propósito es facilitar la depuración.

SomeClass Func() {
    // ...stuff...
    var ret = FinalComputationResult(thing, foo, bar);
    // ret is unused, but while debugging allows checking return value.
    return ret;
}

Aquí depende de cuán complejo stuffes y cómo se relaciona eso con el valor, que se devuelve.

Como una "regla de oro", diría lo siguiente:

Si ayuda a comprender el propósito del código, está bien aumentar la redundancia en su código

o para decirlo de otra manera:

Escriba su código tan libre de redundancia como sea necesario para comprender fácilmente más tarde lo que está haciendo el código

Eso da como resultado: La mayoría de las veces, debe eliminar las variables de "depuración" .

Thomas Junk
fuente
19

El mayor problema es la claridad . Si su código tiene un montón de basura extraña, y estoy manteniendo su código, tengo que averiguar qué hace todo.

¿Esa variable se usa alguna vez para algo? Quizás realice operaciones en él, pero nunca use su valor para algo. El simple incremento de una variable no significa que la variable tenga un propósito si nunca la lee (retorno, entrada en otra expresión, et al).

Sin embargo: si una variable tiene un propósito y se nombra adecuadamente , no resta valor al código como un todo.

Dados los ejemplos en su pregunta actualizada y algunas otras que pensé:

  • Tener una función que llama a otra función, almacena el resultado, lo registra y luego lo devuelve es bastante corto y claro.

  • Dividir un enunciado con múltiples expresiones en múltiples enunciados (por ejemplo, dividir un cálculo largo y complejo en múltiples enunciados con múltiples variables intermedias) puede hacer que el código sea más claro a pesar de ser más detallado. Con menos información para procesar a la vez, el cerebro puede asimilar más fácilmente lo que está sucediendo.

  • No usar excepciones está perfectamente bien: la mayoría de los idiomas requieren que se especifique la excepción que se captura, incluido el nombre de una variable para usar en el catchbloque. No hay forma de evitar esto en muchos lenguajes populares y los programadores están acostumbrados.

Si necesito pasar tiempo averiguando qué elementos de código tienen un propósito y cuáles solo están desperdiciando mi tiempo, mi productividad disminuye. Sin embargo, si el código es bastante conciso y las variables están bien nombradas, incluso cuando parecen extrañas al principio, esto mitiga el riesgo de perder tiempo innecesario revisando el código para comprenderlo.

El código incorrecto con variables extrañas, bloques vacíos y código muerto es una forma en que los desarrolladores obtienen una mala reputación en la oficina: "Estaba arreglando un error en el código de Snowman. Pasé dos horas descubriendo qué estaba haciendo esa función, y treinta segundos arreglando ¡el error! ¡Snowman es terrible programando! " Pero si el código tiene un propósito y está escrito de manera clara, está perfectamente bien.


fuente
99
+1 para "Snowman es terrible en la programación". Siempre sospeché. ¿Cómo puedes ser un buen programador cuando tus dedos siempre se están derritiendo?
Robert Harvey
Vea mi edición: no me refiero a meras variables creadas de la nada sin ningún propósito, solo aquellas que pueden ayudar aquí y allá durante la depuración, o simplemente generadas por una de esas herramientas automáticas como los bloques de captura.
Tar
1
@Tar que cambia toda la pregunta: editar entrantes.
Tenga en cuenta que en su lista con viñetas de cuándo hace esto, los dos primeros tienden a no dar lugar a una advertencia (nunca he visto un lenguaje que produzca una advertencia en tal caso). Para el tercer caso, cualquier idioma que requiera que la excepción tenga un identificador no tiene una advertencia, y cualquiera que produzca una advertencia no requiere que tenga un identificador (de nuevo, estoy familiarizado con de todas formas).
Servicio
2
Mirando el historial de revisiones, no veo ningún indicio de que la pregunta cambie fundamentalmente. En lo que respecta al código del OP, los dos primeros generarán una advertencia en C # (que parece ser el idioma utilizado). El tercer ejemplo no daría lugar a una advertencia. Los comentarios sobre la pregunta también indican que están indicados específicamente en las advertencias del compilador, y cuando Robert señaló que el tercer ejemplo no produce una advertencia, el autor admitió que era un mal ejemplo para su pregunta.
Servy
5

Las variables no utilizadas que no tienen un propósito obvio son un olor a código en mi opinión. En el mejor de los casos, pueden ser una distracción: se suman al ruido general en el módulo.

El peor de los casos es que los codificadores posteriores pasan tiempo descubriendo lo que se supone que debe hacer y preguntándose si el código está completo. Hazte un favor (y a todos los demás) y deshazte de él.

Robbie Dee
fuente
1
¿Qué es un "error" en su opinión? Las variables no utilizadas no producen resultados inesperados y erróneos per se, que es lo que la mayoría de las personas calificaría como un error.
Harris
55
¿Por qué no se usa la variable? Muy probablemente porque debería haberse usado, pero en cambio se usó otra variable por error. Obtiene una advertencia del compilador, y lo arregla y corrige el error al mismo tiempo. El problema es que si tiene un programador indisciplinado que deja un desastre, no puede encontrar dónde ese tipo de advertencia indica un error y dónde solo indica un programador desordenado.
gnasher729
@HarrisWeinstein Dado que los probadores y desarrolladores no pueden ni siquiera ponerse de acuerdo sobre qué es un error la mayor parte del tiempo, lo voy a estacionar por ahora. Baste decir que es una deficiencia en el código, y posiblemente en el ejecutable, ya que ocupará espacio. Es cierto que no es el mayor problema en el mundo, pero sí con errores ortográficos en el cuadro de comentarios y código comentado. Es cruft que no agrega nada, por lo que debe eliminarse. El código que se ve bien inspira confianza. Creo que esta cita también es adecuada y cubre bien el proceso de refactorización.
Robbie Dee
Si está utilizando un lenguaje dinámico, es casi seguro que asignar un valor a some_varcuando el resto del código lo use someVarsea ​​un error. Esto puede no ser un problema en Java o C, pero puede romper fácilmente Python.
Sean McSomething
1
@ThomasJunk Ese es probablemente un mejor término para eso, gracias. Ajustaré mi respuesta en consecuencia.
Robbie Dee
5

Las variables no utilizadas hacen que la intención de su código no sea clara. Esto es malo porque a pesar de las apariencias, el código está escrito principalmente para que las personas lo lean, no para las computadoras .

Otros ya han señalado que construir un valor y no usarlo confunde a otras personas que tienen que leer y trabajar con su código. Sin embargo, en mi opinión, el mayor peligro es para usted mismo .

Una variable no utilizada puede ser intencional, o puede ser un descuido que apunta a un defecto. Por ejemplo, es posible que haya escrito mal un nombre y almacenado un valor en un lugar cuando pensó que lo había almacenado en otro. El programa resultante podría funcionar bien pero en silencio daría el resultado incorrecto.

Analizar el flujo de datos puede ayudarlo a encontrar tales errores y muchos otros. Por lo tanto, vale la pena escribir su código de tal manera que todo lo que un analizador de flujo de datos señale como una anomalía sea, de hecho, un error. La asistencia automática para prevenir errores es invaluable; mucha gente piensa "Oh, no necesito ayuda, nunca sería tan descuidado", pero hasta ahora todos los que he conocido pensaron que eso estaba mal.

Kilian Foth
fuente
2

Existe un estilo de codificación que implica asignar deliberadamente cualquier cosa que fuera (o pudiera llegar a ser) de interés a una variable con el propósito específico de verla fácilmente en un depurador. Esto es especialmente útil para el código que se ve así:

return foo(bar(), baz(wot(12), wombat(&badger)));

Incluso con nombres algo mejores para las funciones, puede ser más fácil resolver qué está mal en la llamada a foo cuando se escribe como:

auto awombat = wombat(&badger);
auto awot = wot(12);
auto abaz = baz(awot, abadger);
auto abar = bar();
auto afoo = foo(abar, abaz);
return afoo;

Todavía tengo que trabajar con personas que practiquen esto como la configuración predeterminada, pero reescribir un código que se niega obstinadamente a tener sentido en forma de asignación única puede hacer que sea más fácil resolver lo que está sucediendo bajo el depurador. Siempre volvimos el código a un formato compacto y "bonito" después, pero me pregunto si eso fue un error.

La alternativa es caminar arriba y abajo de la pila y esperar que no te alejes demasiado, o usar depuradores reversibles (los angustiantemente raros).

No es una estrategia popular, pero como me ayudó a resolver algunos errores absurdos, no creo que deba descartarse como intrínsecamente malo.

Jon Chesterfield
fuente
Si bien probablemente no iría tan lejos como tú, encuentro tu primera forma "bonita" demasiado compacta.
Loren Pechtel
Excepto que no hay una sola variable no utilizada en su código.
Florian F
En el lado positivo, si recibiera una advertencia sobre las variables no utilizadas, sabría que en algún lugar de esa transformación cometió un error importante. Lo cual sería fácil de arreglar.
gnasher729
1

Para las personas que incluyen su código de prueba de unidad en los mismos proyectos o módulos que su código "real", un campo no utilizado , como un método de objeto, es una señal fuerte de que las pruebas de unidad no están probando todo. Esto aumenta las posibilidades de que un desarrollador intente usar ese campo o método actualmente no utilizado, y se encuentre con un error que no fue detectado hasta entonces.

Una variable no utilizada en una subrutina o método algo trivial puede indicar que el programador pretendía usarla, pero está usando otra variable por error (por ejemplo, después de limpiar después de una operación de copiar y pegar).

Una variable no utilizada en una subrutina más trivial es probablemente escoria innecesaria, como han respondido otras personas. Ocasionalmente, se usa en una declaración de ruta de navegación que actualmente está comentada:

SomeObject tmp = someMethod();
// printDebugInfoAbout (tmp);

... está claro para el globo ocular que se usa allí, y solo allí (es decir, someMethodtiene un efecto secundario importante), pero el IDE no lo sabe, y eliminar esa variable es más problemático de lo que vale. Yo uso provisionalmente supresores de advertencia en tales casos.

Paul Brinkley
fuente
1
Me pregunto en qué circunstancias desearía que el código comentado se enviara a su repositorio. Pero surge una situación similar cuando se usa la compilación condicional (como cuando se usa la assertmacro en C o C ++). Las advertencias variables no utilizadas pueden ser una molestia allí y suprimirlas es la respuesta más razonable, si me preguntas.
5gon12eder
1
He editado mi respuesta, agregando un ejemplo muy aproximado de lo que he visto en el pasado. El bloqueo puede ejecutarse muchas veces, y algo está recopilando toda la información de depuración en un registro para analizar cuando se rastrea un problema, pero la impresión es lenta y, por lo tanto, generalmente está desactivada. Podría reemplazar la primera línea someMethody perder información sobre el origen tmp. O podría confirmar una versión limpia y olvidar que alguna versión anterior tenía esa útil ruta de exploración (y carecía del código necesario agregado en versiones posteriores). ... He buscado una compensación ideal por esto desde hace mucho tiempo.
Paul Brinkley
Veo. Aparentemente, su idioma de elección no tiene un preprocesador. ;-)
5gon12eder
Actualmente sí. :-) Aunque para ser justos, he visto este mismo tipo de cosas en C y C ++, y tampoco fue fácil deshacerse de las variables no utilizadas en esos casos (¿cuasi-apéndices?). ... El hecho de que mi último gran proyecto C / C ++ se realizó en un momento en que los IDE no existían (las advertencias del compilador requerían ejecutar "gcc -pedantic") tampoco ayudó.
Paul Brinkley
0

Es fácil cuando escribe código mezclar dos variables. Calcula una variable B pero luego usa en su lugar alguna variable A creada anteriormente.

Entonces, una posible razón para una variable no utilizada es que hay un error en su código. Es por eso que el compilador te advierte. Si "no haces" variables no utilizadas, incluso es un regalo muerto.

Cuando vea tal advertencia, debe verificar su código y corregirlo o eliminar la variable infractora. Si no lo elimina, dejará de prestar atención a estas advertencias, y podría deshabilitarlas por completo. Pero finalmente perderás una tarde depurando el código debido a un error tonto.

Florian F
fuente