generate - params comments c#
''Goto'' es tan malo? (8)
Después de investigar acerca de cómo romper un ciclo secundario
while (true) { // Main Loop
for (int I = 0; I < 15; I++) { // Secondary loop
// Do Something
break; // Break main loop?
}
}
la mayoría de la gente recomienda llamar a la función ''goto''
Mirando como el siguiente ejemplo:
while (true) { // Main Loop
for (int I = 0; I < 15; I++) { // Secondary Loop
// Do Something
goto ContinueOn; // Breaks the main loop
}
}
ContinueOn:
Sin embargo; A menudo he escuchado que la declaración ''goto'' es una mala práctica. La imagen de abajo está ilustrando perfectamente mi punto:
Asi que
- ¿Qué tan malo es realmente la declaración de goto, y por qué?
- ¿Hay una manera más efectiva de romper el ciclo principal que usar la instrucción ''goto''?
¿Qué tan malo es realmente la declaración de goto, y por qué?
Es realmente malo por todas las razones normales dadas. Está perfectamente bien al emular los bucles etiquetados en idiomas que no los admiten.
Sustituirlo con funciones dispersará en muchos casos la lógica que realmente debería leerse como la misma unidad. Esto hace que sea más difícil de leer. A nadie le gusta seguir un rastro de funciones que realmente no hacen nada hasta el final del viaje, cuando se ha olvidado un poco de dónde comenzó.
Reemplazarlo con booleanos y un montón de ifs y breaks adicionales es realmente torpe y hace que sea más difícil seguir intenciones reales, como cualquier ruido.
En java (y javascript), esto es perfectamente aceptable (loops etiquetados):
outer: while( true ) {
for( int i = 0; i < 15; ++i ) {
break outer;
}
}
En C #, parece que el equivalente más cercano no es:
while( true ) {
for (int I = 0; I < 15; I++) {
goto outer;
}
}
outer:;
Debido a la palabra goto
, que tiene un efecto psicológico de hacer que las personas pierdan todo su sentido común y hacer que vinculen xkcd independientemente del contexto.
¿Hay una manera más efectiva de romper el ciclo principal que usar la instrucción ''goto''?
En algunos casos no es así, por lo que los otros idiomas proporcionan bucles etiquetados y C # proporciona goto
. Tenga en cuenta que su ejemplo es demasiado simple y hace que las soluciones no se vean tan mal porque están adaptadas al ejemplo. De hecho, también podría sugerir esto:
for (int I = 0; I < 15; I++) {
break;
}
Qué tal esto:
int len = 256;
int val = 65536;
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
if (i + j >= 2 * val)
{
goto outer;
}
val = val / 2;
}
}
outer:;
¿Todavía te queda bien?
int len = 256;
int val = 65536;
for (int i = 0; i < len; i++)
{
if (!Inner(i, ref val, len))
{
break;
}
}
private bool Inner(int i, ref int val, int len)
{
for (int j = 0; j < len; j++)
{
if (i + j >= 2 * val)
{
return false;
}
val = val / 2;
}
return true;
}
A veces uso "goto" y me parece que se ve bien, como en el ejemplo anterior;
bool AskRetry(Exception ex)
{
return MessageBox.Show(you know here...) == DialogResult.Retry;
}
void SomeFuncOrEventInGui()
{
re:try{ SomeThing(); }
catch (Exception ex)
{
if (AskRetry(ex)) goto re;
else Mange(ex); // or throw or log.., whatever...
}
}
Sé que puedes hacer lo mismo recursivamente, pero a quién le importa solo funciona y yo uso.
EDITAR:
¿Qué tan malo es realmente la declaración de goto, y por qué?
Depende de la situación exacta. No recuerdo ninguna ocasión en la que descubrí que el código era más legible que la refactorización. También depende de su visión personal de la legibilidad: algunas personas no les gusta más que otras, como se desprende de las otras respuestas. (Como punto de interés, es ampliamente utilizado en código generado ; todo el código async / await en C # 5 se basa en muchos "gotos").
El problema es que las situaciones en las que la tendencia tiende a ser usada tienden a ser el tipo de situaciones en las que la refactorización ayuda a las cosas de todos modos, mientras que goto
pega con una solución que se vuelve más difícil de seguir a medida que el código se vuelve más complicado.
¿Hay una manera más efectiva de romper el ciclo principal que usar la instrucción ''goto''?
Absolutamente. Extraiga su método en una función separada:
while (ProcessValues(...))
{
// Body left deliberately empty
}
...
private bool ProcessValues()
{
for (int i = 0; i < 15; i++)
{
// Do something
return false;
}
return true;
}
Por lo general, prefiero hacer esto antes que introducir una variable local adicional para hacer un seguimiento de "he terminado", aunque eso funcionará, por supuesto.
Estoy de acuerdo con la mayoría de las respuestas sobre qué tan malo es ir.
Mi sugerencia para evitar goto es hacer algo como esto:
while (condition) { // Main Loop
for (int i = 0; i < 15; i++) { // Secondary loop
// Do Something
if(someOtherCondition){
condition = false // stops the main loop
break; // breaks inner loop
}
}
}
Ir a son peligrosos aparte de los otros problemas de mención. La compilación correcta de bypass de Goto en algunos idiomas toma C # por ejemplo, a pesar de no tener declaraciones de retorno en esta función, esto compilará.
public int Test()
{
goto one;
one:
{
goto one;
}
}
Personalmente, me gusta pensar en "ir al infierno" ... y en muchos aspectos esto es cierto, ya que puede conducir a códigos muy poco fáciles de mantener y prácticas realmente malas.
Dicho esto, todavía se implementa por alguna razón, y cuando se usa, debe usarse con moderación, si NO hay otra solución disponible. Los lenguajes OO se prestan a no necesitarlo realmente (mucho).
Las únicas veces que alguna vez lo veo usado en estos días es en ofuscación ... un ejemplo de Goot de usar goto para crear código del infierno, ¡por lo que las personas no podrán entenderlo!
Algunos idiomas dependen de palabras clave equivalentes. Por ejemplo, en x86 assember tiene palabras clave como JMP (Jump), JE (Jump if Equal) y JZ (Jump if Zero). Estos son requeridos frecuentemente en lenguaje ensamblador ya que no hay OO en este nivel, y hay pocos otros métodos para moverse en una aplicación.
AFAIK ... mantente alejado de él a menos que sea ABSOLUTAMENTE NECESARIO.
Un colega mío (que tiene 15 años + en programación de firmware) y uso goto todo el tiempo. ¡Sin embargo, solo lo usamos para manejar excepciones! Por ejemplo:
if (setsockopt(sd,...)<0){
goto setsockopt_failed;
}
...
setsockopt_failed:
close(sd);
Que yo sepa, esta es la mejor manera de manejar excepciones en C. Yo creo que también trabajo para C #. Además, no soy el único que piensa así: ejemplos de buenos gotos en C o C ++
Voy a estar totalmente en desacuerdo con todas las otras respuestas aquí. El código que presentas usando goto
no tiene nada de malo. Hay una razón por la cual C # tiene una declaración goto
, y es precisamente para este tipo de escenarios que usted describe.
goto
simplemente tiene un estigma negativo porque en la década de 1970 y antes la gente escribía un código horrible, completamente inmanejable, donde el flujo de control saltaba por todas partes debido a goto
. goto
''s goto
ni siquiera permite la transición entre los métodos! Sin embargo, todavía existe este estigma irracional contra eso.
En mi opinión, no hay absolutamente nada de malo en usar un goto
"moderno" para salir de un bucle interno. La oferta de personas "alternativas" siempre termina siendo más complicada y más difícil de leer .
En general, se supone que los métodos son reutilizables . Hacer todo un método separado para la parte interna de un bucle, que solo se llamará desde esa ubicación, y donde la implementación del método puede terminar en una ubicación distante en el código fuente, no es una mejora.
Este tipo de ejercicio tonto para evitar goto
es básicamente el equivalente a la corrección política, pero en el mundo de la programación.