2010-05-17 10 views
23

Cuál de los siguientes dos es mejor para el rendimiento y la práctica estándar. ¿Cómo maneja .NET internamente estos dos fragmentos de código?else o return?

Code1

If(result) 
{ 
    process1(); 
} 
else 
{ 
    process2(); 
} 

o código 2

If(result) 
{ 
    process1(); 
    return; 
} 
process2(); 
+2

Gran pregunta, no he visto una pregunta provocar una competencia tan estrecha entre dos enfoques diferentes. – Fenton

Respuesta

25

La diferencia de rendimiento, si la hay, es despreciable en cualquier caso normal.

Una práctica estándar (entre otras) es tratar de mantener un único punto de salida de un método, por lo que habla a favor de la primera alternativa.

La implementación real para el return en el medio es más probable que salte al final del método, donde el código para envolver el marco de pila para el método es, por lo que es probable que el código ejecutable final sea idéntico para los dos códigos.

+4

Estoy de acuerdo con el único punto de salida del método. – Ram

+0

Estoy de acuerdo. Debe tener en cuenta que puede haber efectos secundarios, por ejemplo, si lo hizo en una captura de prueba, podría ejecutar inadvertidamente el código después de la llave. – Doobi

+0

Intento seguir el principio de que el formateo del código en la página debe reflejar la estructura lógica del código. El código 2 anterior oscurece el hecho de que la mitad inferior está en un bloque else "virtual". Hazlo explícito. – JeremyP

1

El retorno hará que el código para volver de cualquier método que la sentencia if se encuentra, se ejecutará ningún código adicional. La declaración sin la declaración simplemente abandonará la declaración if.

2

No puedo decir sobre el rendimiento, pero el código 1 parece mucho más claro y más lógico para mí. Saltar de una función en medio de un bloque if parece bastante confuso y es fácil de pasar por alto.

+0

Entonces, ¿cree que el Código 2 parece mucho más claro, pero no le gusta porque es confuso? –

+0

@Andreas: Ups, error tipográfico. Me refería al Código 1 (y he corregido mi respuesta). Lo siento. –

4

Si hay código común, que necesita ser ejecutado después del bloque if/else, a continuación, la opción 1.

Si el bloque if-else es la última cosa que hacer en una función, a continuación, la opción 2.

Personalmente siempre usar la opción 1. Si hay una stateent de retorno, que viene después de la otra bloquear

3

Si quita las llaves alrededor de código restante de la segunda versión, eso es exactamente lo que yo uso. Prefiero hacer obvia la parte de validación temprana de una función, y luego puedo comenzar a trabajar.

Dicho esto, es una cuestión de opinión. Mientras seas consecuente al respecto, elige uno y mantente con él.

editar: Sobre el rendimiento, el IL emitido es exactamente el mismo. Elija uno u otro para el estilo, no hay penalización para ninguno.

+0

Limpié el código. Creo que quiso decir eso originalmente; No creo que el alcance redundante fuera lo que estaba preguntando. – Will

+0

Sí, pensé, solo quería ser claro. – Blindy

32

Personalmente siempre me gusta volver lo antes posible así que me gustaría ir para algo como:

if (result) 
{ 
    // do something 
    return; 
} 

// do something if not result 

En cuanto a rendimiento, dudo que o bien tienen alguna ventaja sobre unos de otros que realmente se reduce a la legibilidad y el gusto personal. Supongo que .NET optimizaría su primer bloque de código a algo como lo anterior.

+1

Fuera de interés, ¿por qué te gusta 'devolver lo antes posible'? ¿Cuál es el beneficio? – JBRWilkinson

+1

@JBRWilkinson: Aunque la optimización pequeña es probablemente insignificante (como se dijo), siempre siento que es la forma * correcta * de hacer las cosas. ¿Por qué continuar en el método cuando no tienes otro negocio allí? Podría resultar en un procesamiento adicional que no es necesario. – James

+9

Estoy claramente a favor de esta respuesta, al menos en los casos en que el resultado es un control. Evitar la anidación profunda del código mantiene el código de forma más legible y mantenible. Además, he visto un montón de código duplicado en las ramas if/else (por supuesto, no necesariamente). – mattanja

1

me gustaría utilizar Code 1, porque si añado algo más tarde después de la declaración if, sigo siendo positivo se ejecutará, sin mi necesidad de recordar para eliminar la cláusula return desde donde está en Code 2.

+0

Excelente punto. – JBRWilkinson

0

Creo que no importa. Deberías buscar la legibilidad y creo que cuanto menos corchetes, mejor. Así que volvería sin los dos últimos corchetes (ver ejemplo a continuación). Por favor, no piense en el rendimiento cuando escriba algo como esto, no le dará más que demasiada complejidad en una etapa demasiado temprana.

if(result) 
{ 
    process 1 
    return; 
} 

process 2 
0

Creo que la segunda opción es mejor para casos de muchas condiciones (como la validación). Si utiliza el primero en estos casos, obtendrá una sangría fea.

if (con){ 
    ... 
    return; 
} 
if (other con){ 
    ... 
    return; 
} 
... 
return; 
1

Ambos estilos son comunes, y las guerras religiosas se han librado por ellos. :-)

hago normalmente esto:

  • Si la prueba está expresando la semántica de contrato de métodos, por ejemplo, comprobación de los parámetros de entrada para la validez, a continuación, elija la opción 2.
  • De lo contrario, elija la opción 1.

Sin embargo, podría decirse que una regla más importante es "que es más fácil de leer y/o fácil de mantener para la próxima desarrollador que mira ¿el código?".

La diferencia de rendimiento es insignificante, como han dicho otros.

0

Tiendo a tener un solo punto de salida, lo cual es muy útil cuando implemente bloqueos en un entorno de subprocesos múltiples, para garantizar que se liberen los bloqueos. Con la primera implementación, es más difícil de hacer.

+0

¿No se desbloquean automáticamente (suponiendo que esté utilizando el bloqueo {}) cuando están fuera del alcance actual, de forma similar a using() {}? No me imagino que se comporte de otra manera. –

1

Creo que no deberías preocuparte por el rendimiento aquí. En este caso, la legibilidad y la capacidad de mantenimiento son mucho más importantes.

Es una buena práctica atenerse a un punto de salida para una rutina.

Sin embargo, a veces los resultados múltiples solo hacen que el código sea mucho más claro, especialmente cuando tienes un número de pruebas cerca del comienzo del código (es decir, si todos los parámetros de entrada están en el formato correcto). si-verdadero 'debe conducir a un retorno.

es decir:

if (date not set) return false; 
age = calculateAgeBasedOnDate(); 
if (age higher than 100) return false; 
...lots of code... 
return result; 
0

que tienden a tener múltiples puntos de salida de una función. Personalmente creo que es más claro y en algunos casos puede ser más rápido. Si revisas algo y luego regresas, el programa no ejecutará ningún comando de la izquierda. Por otra parte, como HZC dijo que si trabaja en aplicaciones de subprocesos múltiples, su mejor opción podría ser utilizar su primer ejemplo. De todos modos, para pequeños trozos de código no hará ninguna diferencia (probablemente ni siquiera para algunos más grandes). Lo más importante es que escribas cómo te sientes cómodo.

23

Creo que el 'punto de salida único' está sobrevalorado. Mantenerlo demasiado dogmáticamente puede conducir a un código muy complejo, que en realidad debería tener múltiples puntos de salida o dividirse en métodos más pequeños.

Yo diría que la elección entre los dos depende de la semántica.

'Si alguna condición es verdadera, haz esto, de lo contrario haz' mapas perfectamente en if-else.

'Si hay alguna condición, haga algo de limpieza y rescate' se correlaciona mejor con el Código 2. Esto se llama patrón de protección. Esto deja el cuerpo del código como normal, claro y despejado por indentaciones innecesarias como sea posible. Se usa comúnmente para validar parámetros o estados (verificar si algo es nulo, o algo está en caché, cosas así).

if (user == null) { 
    RedirectToLogin(); 
    return; 
} 

DisplayHelloMessage(user.Name); 

No es raro ver ambas formas utilizadas en el mismo proyecto. Cuál usar, como digo, depende de lo que intentas transmitir.

+3

Mi opinión es que tener un solo punto de salida no cambia el hecho de que hay múltiples formas de transitar y, finalmente, salir de la función. El hecho de que todos salgan en el mismo punto no es intrínsecamente mejor que salir antes en varios lugares. Prefiero la salida anticipada, a menos que sea un if-else simple (sin anidamiento) o una salida anticipada conduzca a la duplicación del código. –

0

Si tuviera que decirlo, diría que la mejor práctica es el if-else en lugar del "implícito" else. La razón es que si alguien más modifica su código, pueden atraparlo fácilmente al echarle un vistazo.

Recuerdo que hay un gran debate en el mundo de la programación sobre si debe tener múltiples instrucciones de devolución en su código. Se podría decir que es una fuente de gran confusión porque si tienes múltiples bucles dentro de la declaración "if" y tienes devoluciones condicionales, entonces podría causar cierta confusión.

Mi práctica habitual es tener una instrucción if-else y una declaración de devolución única.

Por ejemplo,

type returnValue; 
if(true) 
{ 
returnValue = item; 
} 
else 
returnValue = somethingElse; 

return returnValue; 

En mi opinión lo anterior es ligeramente más legible. Sin embargo, ese no es siempre el caso. A veces es mejor tener una declaración de devolución en el medio de la declaración if, especialmente si requiere una declaración de devolución engañosa.

+0

Si su función es demasiado complicada para ver fácilmente dónde están los múltiples puntos de salida, tiene problemas mayores que los puntos de salida múltiples. La función necesita descomponerse. – ICR

3

Ambos se compilarán en la misma IL para el modo Release (puede haber algunos operandos Nop diferentes en Debug ..) Y, como tal, no tendrán ninguna diferencia de rendimiento. Esto depende totalmente de cómo usted y su equipo creen que el código es más fácil de leer.

Solía ​​estar en el campamento contra la salida anticipada, pero ahora creo que puede hacer que el código sea mucho más simple de seguir.

// C# 
public static void @elseif(bool isTrue) 
{ 
    if (isTrue) 
     Process1(); 
    else 
     Process2(); 
} 
// IL 
.method public hidebysig static void elseif(bool isTrue) cil managed 
{ 
    // Code size  15 (0xf) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: brfalse.s IL_0009 
    IL_0003: call  void elseif.Program::Process1() 
    IL_0008: ret 
    IL_0009: call  void elseif.Program::Process2() 
    IL_000e: ret 
} // end of method Program::elseif 


// C# 
public static void @earlyReturn(bool isTrue) 
{ 
    if (isTrue) 
    { 
     Process1(); 
     return; 
    } 
    Process2(); 
} 
// IL 
.method public hidebysig static void earlyReturn(bool isTrue) cil managed 
{ 
    // Code size  15 (0xf) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: brfalse.s IL_0009 
    IL_0003: call  void elseif.Program::Process1() 
    IL_0008: ret 
    IL_0009: call  void elseif.Program::Process2() 
    IL_000e: ret 
} // end of method Program::earlyReturn 
0

Depende de lo que sean "resultado", "proceso1" y "proceso2".

Si process2 es la consecuencia lógica de "resultado" es falso; debe ir al Código 1. Este es el patrón más legible si el proceso1 y el proceso2 son alternativas equivalentes.

if (do_A_or_B = A) 
    A() 
else 
    B() 

Si el resultado es un poco de "no-go" condición y "proceso1" no es más que una limpieza necesaria en tal condición debe elegir Código 2. Es el patrón más legible si "process2" es la acción principal de la función.

if (NOT can_do_B) 
{ 
    A() 
    return 
} 

B() 
0

Depende del contexto.

Si está en una función, al calcular un valor, devuelva ese valor tan pronto como lo tenga (opción 2). Está demostrando que ha hecho todo el trabajo que necesita y se va.

Si se trata de un código que forma parte de la lógica del programa, es mejor ser lo más preciso posible (opción 1). Alguien que mire la opción 1 sabrá que quiere decir (haga esto O eso), mientras que la opción 2 podría ser un error (en esta condición, haga esto, SIEMPRE HAGA SIEMPRE eso; ¡solo lo corregiré para usted!). Tenía eso antes.

Cuando se compilan, estos serán generalmente los mismos, pero no estamos interesados ​​en el rendimiento. Esto es sobre legibilidad y mantenible.

2

Aquí hay un poco de lectura adicional sobre la cláusula de protección: http://www.c2.com/cgi/wiki?GuardClause

Un término que no vi mencionado que creo que es importante - el propósito de la cláusula de protección es para aumentar la legibilidad. Los métodos de salida únicos pueden tender hacia el código de "flecha" (donde el anidamiento de las declaraciones forma una punta de flecha).