¿Por qué se considera una mala práctica omitir llaves?

¿Por qué todos me dicen que escribir código como este es una mala práctica?

if (foo) Bar(); //or for(int i = 0 i < count; i++) Bar(i); 

Mi mayor argumento para omitir las llaves es que a veces puede ser el doble de líneas con ellos. Por ejemplo, aquí hay un código para pintar un efecto de brillo para una etiqueta en C #.

 using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor))) { for (int x = 0; x <= GlowAmount; x++) { for (int y = 0; y <= GlowAmount; y++) { g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y)); } } } //versus using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor))) for (int x = 0; x <= GlowAmount; x++) for (int y = 0; y <= GlowAmount; y++) g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y)); 

También puede obtener el beneficio adicional de encadenar los usings juntos sin tener que usings sangría un millón de veces.

 using (Graphics g = Graphics.FromImage(bmp)) { using (Brush brush = new SolidBrush(backgroundColor)) { using (Pen pen = new Pen(Color.FromArgb(penColor))) { //do lots of work } } } //versus using (Graphics g = Graphics.FromImage(bmp)) using (Brush brush = new SolidBrush(backgroundColor)) using (Pen pen = new Pen(Color.FromArgb(penColor))) { //do lots of work } 

El argumento más común para las llaves se basa en la progtwigción de mantenimiento, y los problemas que se producirían al insertar código entre la statement if original y su resultado previsto:

 if (foo) Bar(); Biz(); 

Preguntas:

  1. ¿Es incorrecto querer usar la syntax más compacta que ofrece el idioma? Las personas que diseñan estos idiomas son inteligentes, no puedo imaginar que pongan una característica que siempre es mala de usar.
  2. ¿Deberíamos o no deberíamos escribir el código para que el mínimo común denominador pueda entenderlo y no tenga problemas para trabajar con él?
  3. ¿Hay otro argumento que me falta?

En realidad, el único momento que realmente me mordió fue cuando estaba depurando y comentando barra ():

 if(foo) // bar(); doSomethingElse(); 

Aparte de eso, tiendo a usar:

 if(foo) bar(); 

Que se ocupa del caso anterior.

EDITAR Gracias por aclarar la pregunta, estoy de acuerdo, no deberíamos escribir el código en el mínimo común denominador.

Velocidad de lectura …

Aparte de lo que ya se ha mencionado. En este punto, ya he sido condicionado para analizar declaraciones con llaves y espacios en blanco. Entonces leo:

 if (condition) { DoSomething(); } DoSomethingElse(); 

Un poco más rápido de lo que leo:

 if (condition) DoSomething(); DoSomethingElse(); 

Lo leo un poco más lento si se ve así:

 if (condition) DoSomething(); DoSomethingElse(); 

Leo esto significativamente más lento que el anterior:

 if (condition) DoSomething(); DoSomethingElse(); 

Porque no puedo evitar leerlo nuevamente por si acaso y preguntarme si el autor tenía la intención de:

 if (condition) { DoSomething(); DoSomethingElse(); } 

Ya está cubierto en general, pero cuando se trata de leer el siguiente, voy a investigar esto durante bastante tiempo para asegurarme de lo que el autor pretendió. Incluso puedo buscar al autor original para confirmarlo.

 if (condition) DoSomething(); DoSomethingElse(); 

Si es algo pequeño, escríbelo así:

 if(foo()) bar(); 

Si es lo suficientemente largo como para dividirse en dos líneas, use llaves.

También solía pensar que es mejor usar aparatos ortopédicos cuando es realmente necesario. Pero ya no, la razón principal, cuando tienes un montón de código, lo hace más legible y puedes analizar más rápido el código cuando tienes un estilo de refuerzo consistente.

Otra buena razón para usar siempre llaves, además de que alguien agregue una segunda statement al if, es algo como esto podría suceder:

 if(a) if(b) c(); else d(); 

¿Notaste que la cláusula else es en realidad la del “si (b)”? Probablemente lo hiciste, pero ¿confiarías en que alguien esté familiarizado con esto?

Entonces, si por coherencia y porque nunca se sabe qué cosas inesperadas pueden suceder cuando alguien más (siempre son los otros estúpidos) cambia el código, siempre pongo llaves, porque hace que el código fuente sea más legible, más rápido de analizar tu cerebro. Solo para las declaraciones if más simples, como por ejemplo si se hace una delegación o es similar a un interruptor, donde usted sabe que la cláusula nunca se extenderá, omitiría las llaves.

Prefiero la claridad que ofrece el corsé. Usted sabe exactamente lo que significa y no tiene que adivinar si alguien simplemente cometió un error y lo dejó (e introdujo un error). La única vez que los omito es cuando coloco el if y la acción en la misma línea. Yo tampoco hago eso muy a menudo. En realidad, prefiero el espacio en blanco introducido al colocar la llave en su propia línea, aunque a partir de años de progtwigción tipo C de K & R, terminar la línea con un corsé es una práctica que tengo que superar si el IDE no la aplica para yo.

 if (condition) action(); // ok by me if (condition) // normal/standard for me { action(); } 

Esto no siempre se considera una mala práctica. Las Pautas de encoding de Mono Project sugieren no utilizar llaves si no es necesario. Lo mismo para los Estándares de Codificación GNU . Creo que es una cuestión de gusto personal como siempre con los estándares de encoding.

Las líneas son baratas. La potencia del procesador es barata. El tiempo del desarrollador es muy caro.

Como regla general, a menos que esté desarrollando alguna aplicación absolutamente crítica de recursos / velocidad, siempre me equivocaría al escribir código que es

(a) Fácil para cualquier otro desarrollador seguir lo que estoy haciendo

(b) Comente partes específicas del código que puedan necesitarlo

(c) Fácil de depurar si algo sale mal

(d) Fácil de modificar si es necesario en el futuro (es decir, agregar / eliminar código)

La velocidad o elegancia académica del código es secundaria a estos factores desde una perspectiva comercial. Esto no quiere decir que me puse a escribir código torpe o desagradable, pero este es MI orden de prioridad.

Al omitir las llaves, en la mayoría de los casos, para mí hace que (b), (c) y (d) sean más difíciles (sin embargo, no es imposible). Diría que el uso de llaves no tiene efecto en (a).

Creo que es una cuestión de pautas para el proyecto en el que está trabajando y su gusto personal.

Normalmente los omito cuando no son necesarios, excepto en algunos casos como los siguientes:

 if (something) just one statement; // i find this ugly else { // many // lines // of code } 

yo prefiero

 if (something) { just one statement; // looks better:) } else { // many // lines // of code } 

Uno de los casos en que esto puede morderlo está en los viejos tiempos de las macros C / C ++. Sé que esta es una pregunta de C #, pero a menudo los estándares de encoding se mantienen sin las razones por las que se creó el estándar en primer lugar.

Si no tiene mucho cuidado al crear sus macros, puede terminar causando problemas con las declaraciones if que no usan {}.

 #define BADLY_MADE_MACRO(x) function1(x); function2(x); if (myCondition) BADLY_MADE_MACRO(myValue) 

Ahora, no me malinterpreten, no estoy diciendo que siempre debes hacer {} solo para evitar este problema en C / C ++, pero he tenido que lidiar con algunos errores muy extraños debido a esto.

Yo solía pensar de la misma manera.

Hasta que un día (¿por qué siempre hay ese “un día” que cambia tu vida para siempre?) Pasamos de 24 a 36 horas seguidas sin dormir, depurando el código de producción solo para descubrir que alguien no usó llaves combinadas con un cambio de búsqueda / reemplazo .

Fue algo como esto.

  if( debugEnabled ) println( "About to save 1 day of work to some very important place."); saveDayData(); 

Lo que vino después fue

  if( debugEnabled ) // println( "About to save 1 day of work to some very important place."); saveDayData(); 

Resultó que el sistema generaba 500 mb de registros diarios y se nos pidió que lo detuviéramos. La bandera de depuración no era suficiente, por lo que una búsqueda y reemplazo de println estaba en orden.

Aún cuando la aplicación entró en producción, la bandera de depuración estaba apagada y nunca se llamó al importante “saveDayData”.

EDITAR

Ahora, el único lugar donde no utilizo los frenos es en la construcción if / try.

 if( object != null ) try { object.close(); } catch( ..... 

Después de ver a un desarrollador superestrella haciendo eso.

Para ser franco lo veo como:

Los buenos progtwigdores progtwign a la defensiva, los progtwigdores malos no.

Dado que hay varios ejemplos arriba y mis propias experiencias similares con errores relacionados con el olvido de aparatos ortopédicos, aprendí la manera difícil de SIEMPRE PONER BRAZOS.

Cualquier otra cosa es elegir estilo personal sobre seguridad y eso es claramente una mala progtwigción.

Joel incluso menciona esto en Hacer que el código incorrecto se vea mal

Una vez que te muerde un error debido a que faltan llaves, te das cuenta de que las llaves faltantes se ven mal porque sabes que es un lugar potencial para que ocurra otro error.

Estoy bastante feliz de:

 foreach (Foo f in foos) foreach (Bar b in bars) if (f.Equals(b)) return true; return false; 

Personalmente, no veo por qué

 foreach (Foo f in foos) { foreach (Bar b in bars) { if (f.Equals(b)) { return true; } } } return false; 

es más legible

Sí, las líneas son gratuitas, pero ¿por qué debería tener que desplazarme por páginas y páginas de código cuando podría tener la mitad del tamaño?

Si hay una diferencia en la legibilidad o mantenibilidad, entonces, seguro, ponga llaves … pero en este caso no veo ninguna razón para hacerlo.

Además, siempre pondré llaves para nesteds si es donde he nested los de más

 if (condition1) if (condition2) doSomething(); else (condition2) doSomethingElse(); 

vs

 if (condition1) if (condition2) doSomething(); else (condition2) doSomethingElse(); 

es terriblemente confuso, así que siempre lo escribo como:

 if (condition1) { if (condition2) doSomething(); else (condition2) doSomethingElse(); } 

Siempre que sea posible, uso operadores ternarios, pero nunca los anida .

Estoy de acuerdo en que “si eres lo suficientemente inteligente como para que alguien te pague por escribir el código, debes ser lo suficientemente inteligente como para no confiar únicamente en la sangría para ver el flujo del código”.

Sin embargo … se pueden cometer errores, y este es un dolor de depurar … especialmente si vienes a buscar el código de otra persona.

Mi filosofía es si hace que el código sea más legible, ¿por qué no hacerlo?

Obviamente, tienes que trazar la línea en algún lugar, como encontrar ese medio feliz entre nombres de variables concisas y excesivamente descriptivas. Pero los corchetes realmente evitan errores y mejoran la legibilidad del código.

Se puede argumentar que las personas lo suficientemente inteligentes como para ser codificadores van a ser lo suficientemente inteligentes como para evitar errores que provocan declaraciones sin corchetes. ¿Pero puedes decir honestamente que nunca te has equivocado con algo tan simple como un error ortográfico? Minutia como este puede ser abrumador cuando se buscan proyectos grandes.

Siempre hay excepciones, pero yo argumentaría en contra de omitir llaves solo cuando está en una de las formas:

 if(x == y) for(/* loop */) { //200 lines } //rampion's example: for(/* loop */) { for(/* loop */) for(/* loop */) { //several lines } } 

De lo contrario, no tengo ningún problema con eso.

De vez en cuando uso el código inferior (múltiples instrucciones de uso), pero aparte de eso siempre pongo los frenos. Solo encuentro que aclara el código. Es descaradamente obvio, más que solo una sangría, que una statement es parte de un bloque (y probablemente sea parte de un si, etc.).

He visto el

 if (...) foo(); bar(); 

me muerde el error (o más bien “yo y colegas” – en realidad no presenté el error) una vez . Esto fue a pesar del hecho de que nuestros estándares de encoding en el momento recomendaban utilizar llaves en todas partes. Me tomó un tiempo sorprendentemente largo de detectar, porque ves lo que quieres ver. (Esto fue hace unos 10 años. Tal vez lo encuentre más rápido ahora).

Por supuesto, si usas “llave al final de la línea”, se reducen las líneas adicionales incurridas, pero personalmente no me gusta ese estilo de todos modos. (Lo uso en el trabajo, y lo he encontrado menos desagradable de lo que esperaba, pero sigue siendo un poco desagradable).

Fuera de las tres convenciones:

 if(addCurleyBraces()) bugFreeSofware.hooray(); 

y:

 if(addCurleyBraces()) bugFreeSofware.hooray(); 

y (que representan cualquier estilo de sangría usando una llave de apertura y de cierre):

 if(addCurleyBraces()) { bugFreeSofware.hooray(); } 

Prefiero el último como:

  • Me resulta más fácil leer si todas las declaraciones if se escriben de manera uniforme.
  • Puede hacer que el software sea un poco más robusto y libre de errores. Sin embargo, todos los IDE modernos y editores de texto avanzados tienen buenas características de sangría automática que creo que todos deberían usar, siempre y cuando no estropeen el formato de los comentarios o vayan en contra de los estándares del equipo (en muchos casos es posible crear un esquema de formateo personalizado y compartirlo con el equipo). Mi punto aquí es que si la sangría se hace correctamente, el riesgo de introducción de errores se reduce un poco.
  • Prefiero que la expresión booleana y la (s) statement (es) se ejecuten para estar en diferentes líneas. Me gusta poder marcar una fila para fines de depuración. Incluso si estoy usando un IDE donde puedo marcar una statement y dar un paso hacia ella, es una operación interactiva y podría olvidar dónde comencé a depurar o al menos me tomará un poco más de tiempo pasar por el código varias veces. veces (ya que tengo que marcar la posición manualmente cada vez durante la depuración).

Estoy impresionado y honrado de que mis compañeros en este campo de la progtwigción de computadoras (usted) no se intimiden por la posibilidad de posibles errores cuando se saltean los apoyos en bloques de una sola línea.

Supongo que significa que no soy inteligente. He cometido errores al respecto varias veces. He depurado los errores de otros alrededor de esto. He visto el software enviar errores debido a esto (RDP a una máquina que ejecuta VS2002 y la fuente de la ventana de su reloj se volverá inestable).

Si veo todos los errores que he cometido y que podrían haberse evitado con un cambio en el estilo de encoding, la lista es muy larga. Si no hubiera cambiado mi enfoque en cada uno de estos casos, probablemente nunca lo habría hecho como progtwigdor. De nuevo, creo que no soy inteligente. Para compensar, he sido un usuario acérrimo de llaves en bloques de una sola línea durante mucho tiempo.

Dicho esto, algunas cosas han cambiado en el mundo que hacen que la regla “uses llaves en bloques de una sola línea” sea menos relevante hoy que cuando Moisés nos la trajo:

  • Algunos lenguajes populares hacen que el problema desaparezca al hacer que la computadora lea la sangría, tal como lo hace el progtwigdor (por ejemplo, Python).

  • Mi editor automáticamente formatea para mí, por lo que las posibilidades de que me induzcan por indentación son muy reducidas.

  • TDD significa que si presento un error porque me confunde un bloque de una línea, es mucho más probable que descubra el error rápidamente.

  • La refactorización y la expresividad del lenguaje significan que mis bloques son mucho más cortos, y los bloques de una sola línea ocurren con mucha más frecuencia que los solía hacerlo. Hipotéticamente, con una aplicación despiadada de ExtractMethod, posiblemente podría tener solo bloques de línea única en todo mi progtwig. (Me pregunto cómo sería eso?)

De hecho, la refactorización sin piedad y la omisión de llaves en bloques de una sola línea puede tener un beneficio distintivo: cuando ve llaves, una pequeña alarma puede sonar en su cabeza que dice “¡complejidad aquí! ¡Cuidado!”. Imagina si esta fuera la norma:

 if (condition) Foo(); // normal, everyday code if (condition) { // something non-trivial hapening; pay attention! Foo(); Bar(); } 

Me abro a la idea de cambiar mi convención de encoding a algo como “los bloques de una sola línea nunca tienen llaves” o “si puedes poner el bloque en la misma línea que la condición, y todo cabe dentro de 80 caracteres, omita los frenos “. Ya veremos.

Sus argumentos principales contra el uso de llaves son que usan líneas adicionales y que requieren sangrado adicional.

Las líneas son (casi) gratuitas, minimizar el número de líneas en su código no debe ser un objective.

Y la indentación es independiente del uso de la abrazadera. En tu ejemplo de “uso” en cascada, sigo creyendo que deberías sangrarlos incluso cuando omitas los frenos.

Creo firmemente en escribir código ordenado y conciso, pero siempre usaré llaves. Encuentro que son una forma conveniente de ver rápidamente el scope en el que existe una línea particular de código. No hay ambigüedad, simplemente se establece explícitamente frente a ti.

Algunos pueden decir que es un caso de preferencia, pero considero que el flujo lógico de un progtwig es mucho más fácil de seguir si es internamente consistente, y no creo que sea consistente escribir una statement de IF como esta;

 if(x < y) x = y; else y = x; 

Y otro como este;

 if(x < y) { x = y; x++; } else { y = x; y++; } 

Prefiero solo elegir un estilo general y atenerme a él 🙂

De acuerdo, esta es una vieja pregunta que ha sido respondida hasta la muerte. Tengo algo que agregar.

Primero solo tengo que decir USAR LOS BRAZOS. Solo pueden ayudar a la lectura, y la legibilidad (¡para usted y para otros!) Debe ser muy alta en su lista de prioridades a menos que esté escribiendo ensamblaje. Código ilegible siempre, siempre conduce a errores. Si encuentra que las llaves hacen que su código ocupe demasiado espacio, sus métodos probablemente sean demasiado largos. La mayoría o todos los métodos deben caber dentro de una altura de pantalla si lo haces bien, y Find (F3) es tu amigo.

Ahora para mi agregado: hay un problema con esto:

 if (foo) bar(); 

Intenta establecer un punto de interrupción que solo se activará si se ejecuta bar (). Puede hacer esto en C # colocando el cursor en la segunda mitad del código, pero eso no es obvio y es un poco molesto. En C ++ no podías hacerlo en absoluto. Uno de nuestros desarrolladores más experimentados que trabaja en código C ++ insiste en romper las declaraciones ‘if’ en dos líneas por este motivo. Y estoy de acuerdo con él.

Entonces haz esto:

 if (foo) { bar(); //It is easy to put a breakpoint here, and that is useful. } 

Uno de los principales problemas es cuando tiene regiones de líneas únicas y líneas no individuales, junto con la separación de la statement de control ( for , if , qué tiene) y el final de la statement.

Por ejemplo:

 for (...) { for (...) for (...) { // a couple pages of code } // which for block is ending here? A good text editor will tell you, // but it's not obvious when you're reading the code } 

Yo solía ser un gran partidario de “las llaves son de visita obligada”, pero desde que adopté las pruebas unitarias, descubrí que las pruebas de mi unidad protegen las declaraciones irreflexivas de los siguientes escenarios:

 if (foo) snafu(); bar(); 

With good unit tests, I can confidently omit curly braces for simple statements to improve readability (yes, that can be subjective).

Alternatively, for something like the above, I would likely inline that to look like:

 if (foo) snafu(); 

That way, the developer who needs to add bar() to the condition, would be more apt to recognize the lack of curly braces, and add them.

Use some personal judgement.

 if (foo) bar(); 

is fine by itself. Unless you’re really worried about morons putting in something like this later:

 if (foo) bar(); baz(); 

If you’re not worried about morons, you’re fine (I’m not — if they can’t get basic code syntax right, this is the least of their problems)>

In exchange, it’s a lot more readable.

The rest of the time:

 if (foo) { bar(); baz(); } 

Which has been my favorite as long as I can remember. Adicionalmente:

 if (foo) { bar(); baz(); } else { qux(); } 

Funciona para mi.

Vertical space by itself isn’t terribly relevant, readability is. The opening brace on a line by itself just stops the conversation for a syntactic element, until your eye moves down to the next line. Not what I like.

Let’s say you have some code:

 if (foo) bar(); 

and then someone else comes along and adds:

 if (foo) snafu(); bar(); 

According to the way it’s written, bar(); is now executed unconditionally. By including the curly braces, you prevent this kind of accidental error. Code should be written in such a way as to make such mistakes difficult or impossible to make. If I was doing a code review and saw the missing braces, especially spread across multiple lines, I would create a defect. In cases where it is justified, keep it on one line so that the chance of making such an error is again kept to a minimum.

Reducing lines is not really a good argument for dropping braces. If your method is too big, it should probably be refactored into smaller pieces or restructured. Doing that will no doubt increase readability more than simply taking out braces.

in order to keep the code with braces from taking up lots of space, I use the technique recommended in the book Code Complete :

 if (...) { foo(); bar(); } else { ... } 

I always omit them when appropriate, such as in your first example. Clean, concise code I can see and understand by glancing at is easier to maintain, debug and understand than code I have to scroll through and read line by line. I think most programmers will agree with this.

It is easy for it to get out of hand if you start doing multiple nesting, if/else clauses and so on, but I think most programmers should be able to tell where to draw the line.

I see it kind of like the argument for if ( foo == 0 ) vs if ( 0 == foo ) . The latter may prevent bugs for new programmers (and maybe even occasionally for veterans), while the former is easier to quickly read and understand when you’re maintaining code.

Most times it is ingrained as a coding standard, whether for a company or an FOSS project.

Ultimately someone else will need to grok your code and it is a major time drain for each developer to have to figure out the specific style of the section of code they are working on.

Also, imagine that someone is going between Python and a Cish language more than once a day… In Python indenting is part of the block symantics of the language and it would be quite easy to make a mistake like the one you quote.

Err on the side of more secure – just one more bug you potentially won’t have to fix.

I personally feel more secure if all of my blocks are wrapped in curlys. Even for one-liners, these are simple notations that easily prevent mistakes. It makes the code more readable in the sense that you clearly see what is in the block as not to confuse the body of the block with the following statements outside of the block.

If I have a one-liner, I typically format it as follows:

 if( some_condition ) { do_some_operation; } 

If the line is just too cumbersome then use the following:

 if( some_condition ) { do_some_operation; }