2010-09-02 42 views
8

1) Sé cómo funcionan las declaraciones if…else if, pero en el siguiente ejemplo ambos métodos son idénticos en lo que respecta al valor resultante. Entonces, ¿importa cuál de los dos métodos use o debería elegir uno semánticamente más cercano a lo que el código está tratando de hacer (aquí supongo que semánticamente los dos métodos son bastante diferentes)? Entonces, ¿qué método usarías y por qué?Cuándo utilizar 'if ... else if' y cuándo usar

protected string GetNumberDescription(int value) 
{ 
    if (value >= veryBigNumber) 
     return veryBigNumberDescription; 
    else if (value >= bigNumber) 
     return bigNumberDescription; 
    else if (value >= smallNumber) 
     return smallNumberDescription; 
    else 
     return ""; 
} 

protected string GetNumberDescription(int value) 
{ 
    if (value >= veryBigNumber) 
     return veryBigNumberDescription; 
    if (value >= bigNumber) 
     return bigNumberDescription; 
    if (value >= smallNumber) 
     return smallNumberDescription; 
    else 
     return ""; 
} 

2) me di cuenta de perdidas de código utiliza el siguiente formato cuando escribiendo if ... else if declaraciones:

if ... 
else if ... 
else ... 

Pero no es (al menos conceptualmente) de manera más correcta:

if ... 
else 
    if ... 
    else ... 
+1

Pregunta similar en el contexto de Java: http://stackoverflow.com/questions/3579918/programming-preference-use-else-ifs-with-multiple-return-statements/ – BalusC

+1

Consulte http: //www.refactoring. com/catalog/replaceNestedConditionalWithGuardClauses.html ... –

+0

Hacer el primero puede provocar problemas cuando alguien inserta una declaración else if en algún lugar dentro que está siendo manejado por el bloque de ifs. El problema es que la redundancia del código y el rendimiento llegan al – Woot4Moo

Respuesta

16
  1. Probablemente deberías usar el primero. Es más aceptado y más lógico. (No le importa nada después si esta condición es verdadera ... indique eso en su código.)
  2. Generalmente es más aceptado, y más compacto y legible, para usar de la primera manera (si no). Python incluso tiene una palabra clave específica para él (elif).
+1

El primero no guarda las evaluaciones innecesarias ya que cada condición verdadera termina con una declaración de devolución. –

+0

@Michael La Voie - sí, lo siento, me acababa de dar cuenta de mi error y lo corrigió ... No me di cuenta inmediatamente de las devoluciones – froadie

+0

de acuerdo, el segundo estilo es solo una mala codificación. –

7

Ellos son semánticamente idénticos. Dudo que haya alguna diferencia en el rendimiento, por lo que la elección es sobre el estilo y la legibilidad. Si tiene curiosidad, compile el código anterior en un ensamblaje y use Reflector para ver si hay alguna diferencia en el IL. Mi suposición es que habría poca diferencia si hubiera alguna.

+0

Creo que el cartel se da cuenta de que el código generado será equivalente. Él pregunta si hay una razón para preferir un * estilo de codificación * por encima del otro. – Karmastan

+0

@Karmastan: OP dice "semánticamente son bastante diferentes". No son semánticamente diferentes. No tengo una opinión firme sobre las diferencias de estilo, solo señalo que significan lo mismo. –

11
protected string GetNumberDescription(int value) 
{ 
    string ret = ""; 

    if (value >= veryBigNumber) 
     ret = veryBigNumberDescription; 
    else if (value >= bigNumber) 
     ret = bigNumberDescription; 
    else if (value >= smallNumber) 
     ret = smallNumberDescription; 

    return ret; 
} 

La pregunta ya está contestada, pero añade esto porque:

  1. Hay ventajas de tener una sola salida de una función.
  2. La misma variable se está probando repetidamente, por lo que el if/elseif es el más apropiado.

Editado para agregar:

Normalmente soy un gran fan de salir de una función temprana en casos como éste:

if (conditionOne) 
{ 
    if (conditionTwo) 
    { 
     if (conditionThree) 
     { 
      interesting_stuff(); 
     } 
    } 
    else 
     boring(); 
} 
return; 

Estoy mucho más feliz de ver esto:

if (!conditionOne) 
    return; 

if (!conditionTwo) 
{ 
    boring(); 
    return; 
} 

if (!conditionThree) 
    return; 

interesting_stuff(); 
return; 

Pero cuando hay salidas en muchos niveles de anidación diferentes, es muy fácil perder una al leer el código.

Además, como se menciona en los comentarios: una única salida significa solo un lugar para poner un punto de interrupción.

+0

¿Podría ampliar su primer punto? Normalmente preferiría lo contrario, para hacer que mi código sea más pequeño. – Karmastan

+2

Porque un retorno actúa como una sentencia de control de flujo, lo que hace que la lectura del código sea más compleja. Si tiene declaraciones de retorno salpicadas en todo el cuerpo de una función, entonces tiene más escenarios de uso en los que pensar cuando intente comprender cómo funciona la función. Si intenta modificar la función y agregarle más código, se limita a colocar las declaraciones al principio del método si siempre desea que se ejecuten, porque si las coloca al final, en algunos casos, la declaración ganó No llegues tan lejos. Pero tal vez necesites analizar el resultado, para que no puedas ubicarlo al principio. – AaronLS

+1

+1 Podemos poner UN punto de interrupción en la declaración individual para verificar el resultado. – garik

6

Es principalmente una cuestión de preferencia. cuando utilizo declaraciones de devolución, no necesita los puntos en mi opinión, ya que está claro que la función termina allí mismo. Todo depende de la situación y el estilo, por lo que no existe una "mejor" forma clara de hacerlo.

+0

+1 por decir que depende de la situación/el estilo: este es el tipo de cosas sobre las que la gente discute cuando cualquiera de ellas es aceptable. –

+0

Estoy de acuerdo hasta cierto punto. En este caso, claramente las condiciones están interrelacionadas y el estilo de los demás si es más defensivo con los errores, por lo que me inclino a decir que esto no es un problema de estilo. – AaronLS

4

Usaría la primera opción, ya que se muestra en el código las comparaciones están "agrupadas" algunas. Para 2, creo que terminan exactamente en el mismo código IL.

Siempre me aseguro de que el senario más común sea el primero en la sentencia if..else, ya que es lo mejor para el rendimiento.

5

Sugeriría la primera, ya que está haciendo una elección lógica entre cuatro posibilidades. Está agrupando la decisión en un bloque if/else if.

Si separa las decisiones como en su segunda función, parece que no están conectadas en absoluto. Claramente lo son, ya que la variable de valor se repite, pero puede no ser necesariamente obvia.

Además, cuando no tenga nada que devolver, devuelva nulo y no una cadena en blanco. En una aplicación que mantengo, todavía es necesario usar if (value == null || value! = "").

+1

string.IsNullOrEmpty (value) :-) – Goblin

+0

Oh, Dios mío, no puedo creer que nunca pensé que la función sería parte de la biblioteca estándar. ¡Gracias! –

13

personalmente usaría

protected string GetNumberDescription(int value) 
    { 
     if (value >= veryBigNumber) 
      return veryBigNumberDescription; 
     if (value >= bigNumber) 
      return bigNumberDescription; 
     if (value >= smallNumber) 
      return smallNumberDescription; 
     return string.Empty; 
    } 

En realidad, todo depende de lo que su función está haciendo. Si es así de sencillo, manténlo simple. Si es posible que tenga que hacer algo con el resultado antes de devolverlo a continuación, que haría uso de la solución de Egrunin

+0

for (Number number: numberCollection) if (value> = number.value) return number.description; lanza una nueva IllegalArgumentException(); ---- que obviamente debería ser un método que existe dentro de tu clase NumberCollection y se convierte en numberCollection.getDescription (value); No tengas miedo de codificar bien. (No estoy dando a entender que la respuesta no fue más clara/mejor para fines educativos, simplemente dando el siguiente paso) También el número es una de esas palabras que si lo escribe demasiadas veces seguidas no se ve bien. –

4

1) El primero es más frecuente y en la práctica común. Por lo tanto, sería aconsejable apegarse a la práctica estándar.

2) En general, cuando tenemos más de una sentencia condicional, else if se utiliza en las salidas condicionales intermedios y más se utiliza en la última salida condicional. Además, podría ser más eficiente, ya que creo que no tiene que verificar la misma condición cada vez, a diferencia del tipo if-else-if-else ...

Es más como una opción que se le proporciona donde encuentra su propio camino entre muchos caminos hacia el mismo destino. Pero la elección que escuchas o ves a menudo es la que es popular debido a la experiencia de muchos usuarios y expertos.

10

Si las condiciones son mutuamente excluyentes, entonces creo que un patrón else if es lo mejor. Será más difícil romperse accidentalmente a medida que el código evoluciona y se vuelve más complejo, porque su flujo de control representa con mayor precisión la exclusividad mutua de la lógica.

Considere si alguien re-factorizar el primer ejemplo es similar al ejemplo de egrunin tal que sólo hay un retorno, pero tenga en cuenta que se olvidó de añadir el "otro" a cada uno si:

protected string GetNumberDescription(int value) 
{ 
    string ret = ""; 

    if (value >= veryBigNumber) 
     ret = veryBigNumberDescription; 
    if (value >= bigNumber) 
     ret = bigNumberDescription; 
    if (value >= smallNumber) 
     ret = smallNumberDescription; 
    // BUG: very big numbers still fall through to this last condition, 
    //   because they meet all conditions 

    return ret; 
} 

Tienen acaba de presentar un error. Es posible que no se hayan dado cuenta de que los rendimientos eran parte de la lógica que se expresaba. Si bien el error es obvio aquí, en la vida real este código dentro de un par de años podría volverse más complejo, largo y más difícil de leer, de modo que es más fácil cometer ese error.

La detección de errores inesperado

Además, si se deben cumplir al menos una condición (es decir, nunca debe devolver una cadena vacía), entonces me lanzo una excepción en la final más si usted está asumiendo todos los casos debe ser manejado. Esto detectaría un error en el código donde el código cayó a través de todos los demás y no encontró una condición coincidente. Si no lanza una excepción, entonces el error puede causar un comportamiento extraño. ¿Cómo manejará el resto del código la cadena vacía no inicializada? Depende en gran medida del código en cuestión.La idea de lanzar una excepción es causar un error grave exactamente donde está el error para que pueda identificarse y corregirse fácilmente, en lugar de permitir que la aplicación continúe ejecutándose con un retorno no válido.

if (value >= smallNumber) 
    ret = smallNumberDescription; 
else if(...) 
    ret = otherNumberDescription; 
else 
    throw new InvalidOperationException($"No condition matched for value={value}"); 

return ret; 

Si emparejado con excepción de registro apropiado/notificaciones, entonces esto permitiría a tomar conciencia de este error y agregar una condición para manejar los valores previstos. No es realmente necesario para condiciones muy simples, pero es muy útil para encontrar y corregir errores de forma proactiva en este tipo de código.

+1

Esta es la respuesta correcta a la pregunta que hizo el OP. –

4

Las alternativas son bastante cercanas semánticamente, por lo que no importa demasiado. Como se sugiere en algunas respuestas, colocar el resultado en una variable de cadena y regresar al final podría representar mejor lo que está haciendo en el método.

Otra alternativa es utilizar el operando condicional, que le da un código de menos detallado:

protected string GetNumberDescription(int value) { 
    return 
    value >= veryBigNumber ? veryBigNumberDescription : 
    value >= bigNumber ? bigNumberDescription : 
    value >= smallNumber ? smallNumberDescription : 
    String.Empty; 
} 
+0

Creo que esto es más difícil de entender. –

+0

@ Daniel Moura: ¿Por qué? – Guffa

+0

Tengo que pensar para entender este código. Tal vez no estoy acostumbrado a ver este estilo. Usando if ... else if ... else entiendo de inmediato, no tengo que pensar en eso. Uso y veo código como este a> b? c: d y creo que está bien. Al anidar el operando condicional, creo que es más difícil descifrar qué está sucediendo. –

4

Con las declaraciones return, lo que realmente no importa. Sin embargo, estoy totalmente en desacuerdo con aquellos que dicen que el segundo es de alguna manera peor. ¿Qué lo empeora?

En respuesta a su segunda pregunta: tiene toda la razón. De hecho, no solo son las dos formas de formatear if/else ifefectivamente lo mismo; en realidad son literalmente lo mismo. C# no impone un estilo de formato en particular, por lo que estos son todos idénticos:

// This... 
if (A()) 
    return "Congrats!"; 
else if (B()) 
    return "Not bad!"; 
else if (C()) 
    return "Next time..."; 
else 
    return "Uh-oh."; 

// ...is the same as this... 
if (A()) 
    return "Congrats!"; 
else 
    if (B()) 
     return "Not bad!"; 
    else 
     if (C()) 
      return "Next time..."; 
     else 
      return "Uh-oh."; 

// ...which is the same as this... 
if (A()) return "Congrats!"; else 
         if    (B()) 
      return "Not bad!"; 


else 
{ if 
(C()) 


    return 


"Next time...";else{ 
return "Oh-oh."; 
}} 
4
protected string GetNumberDescription(int value) 
{ 
    return new NumberDescriptor(value).toString(); 
} 

Todo lo que necesita es para ocultar su lógica. No tome esto demasiado en serio, pero ... lo haría de esa manera. Lo sé, tu pregunta no se responde aquí, pero ya hay suficientes ejemplos de si/entonces/más en este tema.

3

Yo diría usar el primero si se piensa que la función devuelve una de cuatro posibilidades, todas las cuales se consideran "exitosas". Usaría el último estilo si algunos retornos indicaran "fallas". Por ejemplo (ejemplo de juguete - lanzamiento podría ser mejor para los casos de error):

 
string get_suit_name(int suit) 
{ 
    string suit_names[4] = {"clubs", "diamonds", "hearts", "spades"}; 

    if (0 > suit) /* Avoid LT sign */ 
    return "Suit is improperly negative!"; 
    if (suit >= 4) 
    return "Suit is too big!"; 
    return suit_names[suit]; 
} 
3

Bueno, desde mi punto de vista sería estas prioridades de mayor a menor.

Funcionalidad/lograr el objetivo final Reducción del coste en cualquier ámbito/ nivel de legibilidad

^^^^ nota que muchos disputar esto en mi lista y llamar a la legibilidad por encima de reducción de costes, que realmente depende de tu trabajo envi y el objetivo final, pero creo que es el más común. Pero supongo que viene de un novato que no significa mucho eh: D

Después de eso, es casi cacahuetes. Lo mismo ocurre? : operador. Si estás convencido de que es más legible, ve por ello. En cuanto a la función, realmente no importa. Pero if() {} else {} es significativamente diferente de if() {} else if() {} Así que elija cuál es el código óptimo (resultados más buscados menos resultados inesperados - 0.5 * puntaje de legibilidad) más o menos lo que iría en.

Al ver que el código hace lo mismo para usted, un nuevo atributo entra en juego, ¿se editará este código en una etapa posterior? Y si es así, ¿qué sería mejor elegir? No olvide el futuro del código, ni los comentarios, que una bonificación gratuita para elegir junto con él.Incluso puede hacer ambas cosas si no puede resolverlo por el tiempo y simplemente comentar el otro, pero eso deja paredes de texto descuidadas que se eliminarán más adelante en la limpieza/liberación.

3

Para el ejemplo proporcionado, no es necesario que preocuparse de "if" si usted está dispuesto a sacrificar una tonelada de legibilidad y, posiblemente, un poco de velocidad:

string [] descriptions = {"", smallNumberDescription, bigNumberDescription, veryBigNumberDescription}; 

protected string GetNumberDescription(int value) 
{ 
    int index = Convert.ToInt32(value >= smallNumber) + Convert.ToInt32(value >= bigNumber) 
       + Convert.ToInt32(value >= veryBigNumber); 
    return descriptions[index]; 
} 
3

En términos de rendimiento, pueden ser los mismos dependiendo del compilador/intérprete. En mi opinión, el segundo es mejor ... mucho más fácil de depurar.

5

Desea utilizar "else if" porque solo se activará si no se acepta el primer "si". En su caso, los 3 enunciados if son correctos, simplemente porque si un último "si" es correcto, entonces el "si" anterior no puede ser.

En caso de que todas sus declaraciones if sean correctas, querría usar else if.

Por ejemplo:

if (8<10) { 
return 10; 
} 
if (8<9) { 
return 9; 
} 

Eso es un ejemplo muy simplista, pero se entiende la idea. Utiliza "else if" cuando desea que solo se active una declaración "if".

1

Pues bien, en su caso particular, sugeriría esto:

protected string GetNumberDescription(int value) { 
    if (value >= veryBigNumber) 
     return veryBigNumberDescription; 
    if (value >= bigNumber) 
     return bigNumberDescription; 
    if (value >= smallNumber) 
     return smallNumberDescription; 

    return ""; 

}

Puesto que, la que volverá de la función en cualquiera de la verdadera condición, no hay necesidad de utilizar ' de lo contrario si '. Generalmente, 'else if' se usa cuando las dos condiciones no son mutuamente excluyentes, lo que no es el caso aquí. En su caso, si alguna de las condiciones se convierte en verdad, el código no se ejecutará más. Además, no se requiere el último "más", ya que el control llegará a ese punto solo cuando todas las condiciones anteriores hayan sido falsas.

Todo es cuestión de dónde lo esté usando y cómo. El motivo principal es maximizar la legibilidad de su código y hacerlo lo más elegante y fácil de entender posible.