2009-08-16 24 views
73

imaginar el siguiente código:¿Es una mala práctica usar el retorno dentro de un método de vacío?

void DoThis() 
{ 
    if (!isValid) return; 

    DoThat(); 
} 

void DoThat() { 
    Console.WriteLine("DoThat()"); 
} 

¿Está bien usar un retorno dentro de un método vacío? ¿Tiene alguna penalización de rendimiento? O sería mejor escribir un código como este:

void DoThis() 
{ 
    if (isValid) 
    { 
     DoThat(); 
    } 
} 
+1

¿Qué pasa: DoThis void() {if (isValid) DoThat(); } – Dscoduc

+25

imagina el código? ¿Por qué? ¡Está justo ahí! :-D – STW

+0

Esta es una buena pregunta, siempre pienso que es una buena práctica usar el retorno; para salir del método o función. Especialmente en un método de minería de datos LINQ con múltiples resultados IQueryable y todos ellos dependen el uno del otro. Si alguno de ellos no tiene resultado, alerta y sal. – Cheung

Respuesta

2

No hay ninguna penalización en el rendimiento, sin embargo, la segunda pieza de código es más legible y por lo tanto más fácil de mantener.

+0

Russell No estoy de acuerdo con su opinión, pero no debería haber sido votado negativamente por ella. +1 para emparejarlo. Por cierto, creo que una prueba booleana y retorno en una sola línea seguida de una línea en blanco es una clara indicación de lo que está sucediendo. p.ej. Primer ejemplo de Rodrigo. –

+0

No estoy de acuerdo con esto. El aumento de la anidación no mejora la legibilidad. La primera parte del código está usando una declaración de "guardia", que es un patrón perfectamente comprensible. – cdmckay

+0

No estoy de acuerdo también. Las cláusulas de protección que rescatan pronto de una función generalmente se consideran algo bueno hoy en día para ayudar al lector a comprender la implementación. –

147

Un retorno en un método nulo no es malo, es una práctica común en invert if statements to reduce nesting.

Y tener menos anidamiento en sus métodos mejora la legibilidad y facilidad de mantenimiento del código.

En realidad, si tiene un método nulo sin ninguna declaración de devolución, el compilador siempre generará un ret instruction al final de este.

+13

¡Corto, dulce y correcto! – bdd

2

Está perfectamente bien y no hay 'penalización de rendimiento', pero nunca escriba una declaración 'si' sin corchetes.

Siempre

if(foo){ 
    return; 
} 

Es la forma más fácil de leer; y nunca asumirá accidentalmente que algunas partes del código están dentro de esa declaración cuando no lo están.

+2

legible es subjetivo. imho, cualquier cosa que se agregue al código que es innecesario lo hace menos legible ... (Tengo que leer más, y luego me pregunto por qué está ahí y perder el tiempo tratando de asegurarme de que no me falta algo) ... pero ese es mi opinión subjetiva –

+10

La mejor razón para incluir siempre las llaves es menos sobre la legibilidad y más sobre la seguridad. Sin las llaves, todo es más fácil para alguien más tarde arreglar un error que requiere declaraciones adicionales como parte del si, no prestar suficiente atención y agregarlas sin agregar también llaves. Al incluir siempre los refuerzos, este riesgo se elimina. –

+2

Sedoso, por favor presione enter antes de su '{'. Esto alinea tu '{' con tu '}' en la misma columna, lo que ayuda enormemente a la legibilidad (es mucho más fácil encontrar las llaves de apertura/cierre correspondientes). – Imagist

17

Mala práctica ??? De ninguna manera. De hecho, siempre es mejor manejar validaciones volviendo del método lo antes posible si las validaciones fallan. De lo contrario, resultaría en una gran cantidad de objetos anidados &. Terminar temprano mejora la legibilidad del código.

También puedes ver las respuestas en una pregunta similar: Should I use return/continue statement instead of if-else?

1

En este caso, el segundo ejemplo es un código mejor, pero eso no tiene nada que ver con el regreso de una función void, es simplemente porque el segundo código es más directo. Pero regresar de una función nula es completamente correcto.

6

No es una mala práctica (por todos los motivos ya mencionados). Sin embargo, cuantas más vueltas tengas en un método, es más probable que se divida en métodos lógicos más pequeños.

-1

Voy a estar en desacuerdo con todos ustedes jóvenes traficantes en este.

Utilizar el retorno en medio de un método, vacío o no, es una práctica muy mala, por razones que fueron claramente definidas, hace casi cuarenta años, por el difunto Edsger W. Dijkstra, comenzando en el conocido " Declaración de GOTO considerada dañina ", y continúa en" Programación estructurada ", por Dahl, Dijkstra y Hoare.

La regla básica es que cada estructura de control y cada módulo deben tener exactamente una entrada y una salida. Un retorno explícito en el medio del módulo rompe esa regla, y hace que sea mucho más difícil razonar sobre el estado del programa, lo que a su vez hace que sea mucho más difícil decir si el programa es correcto o no (que es una propiedad mucho más fuerte). que "si parece funcionar o no").

"La declaración GOTO considerada dañina" y la "Programación estructurada" iniciaron la revolución de la "Programación estructurada" de los años setenta.Esas dos piezas son las razones por las que tenemos if-then-else, while-do y otras construcciones de control explícitas hoy en día, y por qué las declaraciones GOTO en lenguajes de alto nivel se encuentran en la lista de especies en peligro de extinción. (Mi opinión personal es que necesitan estar en la lista de Extinct Species.)

Vale la pena señalar que el modulador de flujo de mensajes, la primera pieza de software militar que alguna vez aprobó las pruebas de aceptación en el primer intento, sin desviaciones , renuncias, o "sí, pero" verborrea, se escribió en un lenguaje que ni siquiera tenía una declaración GOTO.

También vale la pena mencionar que Nicklaus Wirth cambió la semántica de la declaración RETURN en Oberon-07, la última versión del lenguaje de programación Oberon, convirtiéndolo en una pieza final de la declaración de un procedimiento escrito (es decir, función) , en lugar de una declaración ejecutable en el cuerpo de la función. Su explicación del cambio dijo que lo hizo precisamente porque la forma anterior FUE una violación del principio de una salida de la Programación Estructurada.

+0

¿Cómo encajan las excepciones aquí? – nairdaen

+1

@John: superamos el mandato de Dykstra sobre las devoluciones múltiples justo cuando superamos a Pascal (la mayoría de nosotros, de todos modos). –

+0

Los casos en que se requieren múltiples devoluciones son con frecuencia signos de que un método está tratando de hacer demasiado y debe reducirse. No voy a llegar tan lejos como John con esto, y una declaración de devolución como parte de la validación de parámetros puede ser una excepción razonable, pero entiendo de dónde viene la idea. – kyoryu

4

El primer ejemplo es el uso de una declaración de protección. De Wikipedia:

En la programación de computadoras, un guardia es una expresión booleana que debe evaluar a cierto si la ejecución del programa es continúan en la rama en cuestión.

Creo que tener un montón de guardias en la parte superior de un método es una manera perfectamente comprensible de programar. Básicamente dice "no ejecutes este método si alguno de estos es verdadero".

Así que, en general, se desea:

void DoThis() 
{ 
    if (guard1) return; 
    if (guard2) return; 
    ... 
    if (guardN) return; 

    DoThat(); 
} 

Creo que es mucho más fácil de leer a continuación:

void DoThis() 
{ 
    if (guard1 && guard2 && guard3) 
    { 
    DoThat(); 
    } 
} 
26

hay otra gran razón para usar guardias (en contraposición al código anidado) : Si otro programador agrega código a su función, están trabajando en un entorno más seguro.

considerar:

void MyFunc(object obj) 
{ 
    if (obj != null) 
    { 
     obj.DoSomething(); 
    } 
} 

frente:

void MyFunc(object obj) 
{ 
    if (obj == null) 
     return; 

    obj.DoSomething(); 
} 

Ahora, imaginemos otro programador agrega la línea: obj.DoSomethingElse();

void MyFunc(object obj) 
{ 
    if (obj != null) 
    { 
     obj.DoSomething(); 
    } 

    obj.DoSomethingElse(); 
} 

void MyFunc(object obj) 
{ 
    if (obj == null) 
     return; 

    obj.DoSomething(); 
    obj.DoSomethingElse(); 
} 

Obviamente, esto es un caso simple, pero el programador ha añadido un accidente para el programa en la primera instancia (código anidada). En el segundo ejemplo (salida temprana con guardias), una vez que pasa el guardia, su código está a salvo del uso involuntario de una referencia nula.

Claro, un gran programador no comete errores como este (a menudo). Pero es mejor prevenir que curar; podemos escribir el código de una manera que elimine por completo esta fuente potencial de errores. El anidamiento agrega complejidad, por lo que las mejores prácticas recomiendan el código de refactorización para reducir la anidación.

+0

Sí, pero por otro lado, varias capas de anidación, con sus condiciones, hacen que el código sea aún más propenso a errores, la lógica es más difícil de seguir y, lo que es más importante, es más difícil de depurar. Las funciones planas son menos malvadas, IMO. – Skrim

+17

¡Estoy argumentando en * favor * de anidación reducida! :-) –

+0

Estoy de acuerdo con esto. Además, desde un punto de vista de refactorización, es más fácil y seguro refactorizar el método si obj se convierte en una estructura o algo que puede garantizar que no será nulo. –

-1

Lanzar excepción en lugar de devolver nada cuando el objeto es nulo, etc.

Su método espera que el objeto no sea nulo y no es el caso, por lo que debe lanzar una excepción y dejar que la persona que llama maneje eso.

Pero el regreso anticipado no es una mala práctica de lo contrario.

Cuestiones relacionadas