Quiero saber qué se considera una mejor manera de regresar cuando tengo una if
declaración.
Ejemplo 1:
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
else
{
myString = "Name " + myString;
// Do something more here...
return true;
}
}
Ejemplo 2
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
myString = "Name " + myString;
// Do something more here...
return true;
}
Como puede ver en ambos ejemplos, la función volverá, true/false
pero ¿es una buena idea poner una else
declaración como en el primer ejemplo o es mejor no ponerla?
else
.Respuestas:
El ejemplo 2 se conoce como bloque protector. Es más adecuado para devolver / lanzar una excepción temprano si algo salió mal (parámetro incorrecto o estado no válido). En el flujo lógico normal, es mejor usar el Ejemplo 1
fuente
Mi estilo personal es usar el single
if
para los bloques de protección y elif
/else
en el código de procesamiento del método real.En este caso, está utilizando la
myString == null
condición de protección, por lo que tendería a utilizar elif
patrón único .Considere el código que es un poco más complicado:
Ejemplo 1:
Ejemplo 2
En el ejemplo 1, tanto el guardia como el resto del método están en la forma
if
/else
. Compare eso con el Ejemplo 2, donde el bloque de protección está en laif
forma única , mientras que el resto del método usa la formaif
/else
. Personalmente, encuentro que el ejemplo 2 es más fácil de entender, mientras que el ejemplo 1 parece desordenado y con demasiada sangría.Tenga en cuenta que este es un ejemplo artificial y que podría usar
else if
declaraciones para limpiarlo, pero estoy tratando de mostrar la diferencia entre los bloques de protección y el código de procesamiento de la función real.Un compilador decente debería generar la misma salida para ambos de todos modos. La única razón para usar uno u otro es la preferencia personal o para ajustarse al estilo del código existente.
fuente
Personalmente, prefiero el segundo método. Siento que es más corto, tiene menos sangría y es más fácil de leer.
fuente
Mi práctica personal es la siguiente:
Por ejemplo:
Por ejemplo:
La regla de "una salida" también ayuda cuando el cálculo requiere recursos externos que tiene que liberar, o establece que debe restablecer antes de abandonar la función; a veces se agregan más tarde durante el desarrollo. Con múltiples salidas dentro del algoritmo, es mucho más difícil extender todas las ramas correctamente. (Y si se pueden producir excepciones, la liberación / reinicio también se debe poner en un bloqueo final, para evitar efectos secundarios en casos excepcionales ...).
Su caso parece caer en la categoría de "salida rápida antes del trabajo real", y lo escribiría como su versión del Ejemplo 2.
fuente
Prefiero emplear bloques de protección siempre que puedo por dos razones:
En términos generales, prefiero ver métodos donde la funcionalidad principal del método es clara y mínima. Los bloques de protección ayudan a que esto ocurra visualmente.
fuente
Me gusta el enfoque "Fall Through":
La acción tiene una condición específica, cualquier otra cosa es solo el retorno predeterminado "fall through".
fuente
Si tengo una sola condición, no pasaría demasiado tiempo reflexionando sobre el estilo. Pero si tengo múltiples condiciones de guardia, preferiría style2
Picuture esto. Suponga que las pruebas son complejas y que realmente no desea vincularlas en una sola condición de OR para evitar la complejidad:
versus
Aquí hay dos ventajas principales
Está separando el código en una sola función en dos bloques lógicos visuales: un bloque superior de validaciones (condiciones de protección) y un bloque inferior de código ejecutable.
Si tiene que agregar / eliminar una condición, reduce sus posibilidades de estropear toda la escalera if-elseif-else.
Otra ventaja menor es que tiene un par de llaves menos para cuidar.
fuente
Esto debería ser una cuestión de lo que suena mejor.
se expresa mejor entonces
para métodos pequeños no será una gran diferencia, pero para métodos compuestos más grandes puede hacer que sea más difícil de entender ya que no se separará adecuadamente
fuente
do something
incluye una devolución. De lo contrario, el segundo código se ejecutarádo somethingelse
independientemente delif
predicado, que no es cómo funciona el primer bloque.Encuentro irritante el ejemplo 1, porque la declaración de retorno faltante al final de una función de retorno de valor desencadena inmediatamente la bandera "Espera, hay algo mal". Entonces, en casos como este, iría con el ejemplo 2.
Sin embargo, generalmente hay más involucrados, dependiendo del propósito de la función, por ejemplo, manejo de errores, registro, etc. Así que estoy con la respuesta de Lorand Kedves sobre esto, y generalmente tengo un punto de salida al final, incluso al costo de una variable de bandera adicional. Facilita el mantenimiento y las extensiones posteriores en la mayoría de los casos.
fuente
Cuando su
if
declaración siempre regresa, entonces no hay razón para usar otra cosa para el código restante en la función. Hacerlo agrega líneas adicionales y sangría adicional. Agregar código innecesario hace que sea más difícil de leer y, como todos saben, leer el código es difícil .fuente
En su ejemplo,
else
obviamente es innecesario, PERO ...Cuando se pasa rápidamente por las líneas de código, los ojos suelen mirar las llaves y las hendiduras para tener una idea del flujo del código; antes de que se asienten en el código mismo. Por lo tanto, escribir
else
y colocar llaves y sangría alrededor del segundo bloque de código en realidad hace que sea más rápido para el lector ver que esta es una situación "A o B", donde se ejecutará un bloque u otro. Es decir, el lector ve los corchetes y la sangría ANTES de ver elreturn
después de la inicialif
.En mi opinión, este es uno de esos raros casos en los que agregar algo redundante al código en realidad lo hace MÁS legible, no menos.
fuente
Prefiero el ejemplo 2 porque es inmediatamente obvio que la función devuelve algo . Pero aún más que eso, preferiría regresar de un lugar, así:
Con este estilo puedo:
Ver de inmediato que estoy devolviendo algo.
Capture el resultado de cada invocación de la función con solo un punto de interrupción.
fuente
Mi estilo personal tiende a irse
Usando las
else
declaraciones comentadas para indicar el flujo del programa y mantenerlo legible, pero evitando sangrías masivas que pueden llegar fácilmente a través de la pantalla si hay muchos pasos involucrados.fuente
else
cláusulas incluidas, elegiría la última porque el compilador también las ignorará. Aunque dependería deelse if
no aumentar la sangría más que el original una vez, si lo hiciera , estaría de acuerdo con usted. Pero mi elección sería abandonarlo porelse
completo si se permitiera ese estilo.Yo escribiría
Prefiero este estilo porque es más obvio que volvería
userName != null
.fuente
myString
resultado no utilizado . Acabo de copiar la función de OP. Lo cambiaré por algo que parezca más real. La prueba booleana es trivial y no debería ser un problema.Mi regla general es hacer un retorno if sin otra cosa (principalmente por errores) o hacer una cadena completa if-else-if sobre todas las posibilidades.
De esta manera, evito tratar de ser demasiado elegante con mi ramificación, ya que cada vez que termino haciendo esto, codifico la lógica de la aplicación en mis ifs, lo que los hace más difíciles de mantener (por ejemplo, si una enumeración es OK o ERR y escribo todos mis ifs aprovechando el hecho de que! OK <-> ERR se vuelve una molestia agregar una tercera opción a la enumeración la próxima vez).
En su caso, por ejemplo, usaría un simple if, ya que "return if null" seguramente será un patrón común y no es probable que deba preocuparse por una tercera posibilidad (además de null / not null) en el futuro.
Sin embargo, si la prueba fuera algo que hiciera una inspección más profunda del departamento sobre los datos, me equivocaría hacia un if-else completo.
fuente
Creo que hay muchas trampas en el Ejemplo 2, que en el futuro podrían conducir a un código no deseado. Primero, el foco aquí se basa en la lógica que rodea la variable 'myString'. Por lo tanto, para ser explícito, todas las pruebas de condiciones deben realizarse en un bloque de código que tenga en cuenta la lógica conocida y la predeterminada / desconocida .
¿Qué pasa si más tarde se introdujo el código involuntariamente en el ejemplo 2 que alteró significativamente la salida?
Creo que con el
else
bloque que sigue inmediatamente a la comprobación de un valor nulo, los programadores que agregaron las mejoras a las versiones futuras agregarán toda la lógica porque ahora tenemos una cadena de lógica que no fue intencionada para la regla original (que devuelve si el valor es nulo).Presto mi creencia en gran medida a algunas de las pautas de C # en Codeplex (enlace a eso aquí: http://csharpguidelines.codeplex.com/ ) que establecen lo siguiente:
"Agregue un comentario descriptivo si se supone que el bloque predeterminado (si no) está vacío. Además, si no se llega a ese bloque, arroje una InvalidOperationException para detectar cambios futuros que pueden caer en los casos existentes. Esto garantiza un mejor código, porque se han pensado todos los caminos que puede recorrer el código ".
Creo que es una buena práctica de programación cuando se usan bloques lógicos como este tener siempre un bloque predeterminado agregado (si no, caso: predeterminado) para tener en cuenta todas las rutas de código explícitamente y no dejar el código abierto a consecuencias lógicas involuntarias.
fuente
myString
valor y el código que sucede alif
bloque es la lógica resultante si la cadena! = Nulo. Por lo tanto, dado que el foco está en la lógica adicional que manipula ese valor específico , creo que debería encapsularse en unelse
bloque. De lo contrario, esto abre un potencial ya que demostré que se separó involuntariamente la lógica e introdujo consecuencias no deseadas. Puede que no suceda todo el tiempo, pero esta realmente fue una pregunta de estilo de opinión de mejores prácticas .