¿Debo regresar de una función antes o usar una instrucción if? [cerrado]

278

Muchas veces he escrito este tipo de función en ambos formatos, y me preguntaba si un formato es preferible a otro y por qué.

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

o

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

Por lo general, codifico con el primero, ya que así es como funciona mi cerebro mientras programo, aunque creo que prefiero el segundo, ya que se encarga de cualquier manejo de errores de inmediato y me resulta más fácil de leer

    
pregunta Rachel 07.07.2017 - 18:43

19 respuestas

371

Prefiero el segundo estilo. Deje los casos inválidos fuera del camino primero, ya sea simplemente saliendo o generando excepciones según corresponda, ponga una línea en blanco allí, luego agregue el cuerpo "real" del método. Me parece más fácil de leer.

    
respondido por el Mason Wheeler 11.11.2010 - 20:27
155

Definitivamente lo último. El primero no se ve mal ahora, pero cuando obtienes un código más complejo, no puedo imaginar que alguien piense esto:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

es más legible que

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

Admito completamente que nunca entendí la ventaja de los puntos de salida individuales.

    
respondido por el Jason Viers 07.07.2017 - 18:44
31

Depende : en general, no voy a esforzarme al máximo para intentar mover un montón de código y salir de la función antes de tiempo: el compilador generalmente se encargará de eso para mi. Dicho esto, sin embargo, si hay algunos parámetros básicos en la parte superior que necesito y no puedo continuar de otra manera, lo haré antes. Del mismo modo, si una condición genera un bloque gigante if en la función, también tendré una ruptura temprana como resultado de eso también.

Dicho esto, sin embargo, si una función requiere algunos datos cuando se la llama, por lo general voy a lanzar una excepción (ver ejemplo) en lugar de simplemente regresar.

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}
    
respondido por el rjzii 07.07.2017 - 18:44
22

Prefiero la devolución anticipada.

Si tiene un punto de entrada y un punto de salida, entonces siempre debe rastrear todo el código en su cabeza hasta el punto de salida (nunca se sabe si alguna otra pieza de código a continuación hace algo más que el resultado , por lo que hay que rastrearlo hasta que exista). Usted hace que no importa qué rama determina el resultado final. Esto es difícil de seguir.

Con una entrada y existe una opción múltiple, regresa cuando obtiene el resultado y no se molesta en rastrearlo para ver que nadie le hace nada más (porque no habrá nada más desde que regresó) . Es como tener el cuerpo del método dividido en más pasos, cada uno con la posibilidad de devolver el resultado o dejar que el siguiente paso pruebe su suerte.

    
respondido por el 2 revsuser7197 11.11.2010 - 21:50
12

En la programación en C, donde tiene que limpiar manualmente, hay mucho que decir para el retorno de un punto. Incluso si ahora no hay necesidad de limpiar algo, alguien podría editar su función, asignar algo y necesitar limpiarlo antes de regresar. Si eso sucede, será un trabajo de pesadilla revisando todas las declaraciones de devolución.

En la programación en C ++ tienes destructores e incluso ahora guardias de salida de alcance. Todo esto debe estar aquí para garantizar que el código sea seguro ante excepciones en primer lugar, por lo que el código está bien protegido contra la salida anticipada y, por lo tanto, no tiene inconveniente lógico y es simplemente un problema de estilo.

No tengo suficiente conocimiento sobre Java, si "finalmente" se llamará el código de bloqueo y si los finalizadores pueden manejar la situación de la necesidad de garantizar que algo suceda.

C # Ciertamente no puedo responder.

El lenguaje D le brinda guardias de salida de alcance incorporadas adecuadas y, por lo tanto, está bien preparado para la salida anticipada y, por lo tanto, no debería presentar un problema que no sea el estilo.

Por supuesto, las funciones

no deberían ser tan largas en primer lugar, y si tiene una declaración de cambio enorme, es probable que su código también esté mal factorizado.

    
respondido por el CashCow 18.02.2011 - 14:21
8

Devoluciones tempranas para la victoria. Pueden parecer feos, pero mucho menos feos que las envolturas grandes if , especialmente si hay varias condiciones para verificar.

    
respondido por el STW 11.11.2010 - 20:26
8

Uso ambos.

Si DoSomething es de 3-5 líneas de código, entonces el código se ve hermoso usando el primer método de formateo.

Pero si tiene muchas más líneas que eso, entonces prefiero el segundo formato. No me gusta cuando los corchetes de apertura y cierre no están en la misma pantalla.

    
respondido por el azheglov 12.11.2010 - 19:10
7

Una razón clásica para la entrada de una sola entrada es que, de lo contrario, la semántica formal se convertirá en algo feo de lo contrario (la misma razón por la que GOTO se consideraba perjudicial).

Dicho de otra manera, es más fácil razonar acerca de cuándo su software saldrá de la rutina si solo tiene 1 retorno. Lo que también es un argumento en contra de las excepciones.

Por lo general, minimizo el enfoque de retorno temprano.

    
respondido por el Paul Nathan 11.11.2010 - 20:40
6

Personalmente, prefiero hacer comprobaciones de aprobación / rechazo al principio. Eso me permite agrupar la mayoría de las fallas más comunes en la parte superior de la función con el resto de la lógica a seguir.

    
respondido por el nathan 11.11.2010 - 20:26
5

Depende.

El retorno temprano, si hay alguna condición obvia de callejón sin salida para verificar de inmediato, haría que el resto de la función sea inútil. *

Establezca Retval + retorno único si la función es más compleja y podría tener múltiples puntos de salida de lo contrario (problema de legibilidad).

* Esto a menudo puede indicar un problema de diseño. Si encuentra que muchos de sus métodos necesitan verificar algún estado externo / de paramater o similar antes de ejecutar el resto del código, probablemente sea algo que debe ser manejado por la persona que llama.

    
respondido por el Bobby Tables 11.11.2010 - 21:22
2

Usar un If

En el libro de Don Knuth sobre GOTO, leí que da una razón para tener siempre la condición más probable en primer lugar en una declaración if. Bajo el supuesto de que esto sigue siendo una idea razonable (y no una por pura consideración de la velocidad de la era). Yo diría que las devoluciones tempranas no son una buena práctica de programación, especialmente teniendo en cuenta el hecho de que se utilizan con mayor frecuencia para el manejo de errores, a menos que sea más probable que su código falle en lugar de fallar :-)

Si sigues el consejo anterior, deberías poner ese retorno en la parte inferior de la función, y entonces es mejor que ni siquiera lo llames un retorno allí, simplemente configura el código de error y devuélvelo dos líneas. . De este modo se consigue la 1 entrada 1 salida ideal.

Delphi Specific ...

Creo que esta es una buena práctica de programación para los programadores de Delphi, aunque no tengo ninguna prueba. Pre-D2009, no tenemos una forma atómica de devolver un valor, tenemos exit; y result := foo; o simplemente podríamos lanzar excepciones.

Si tuvieras que sustituir

if (true) {
 return foo;
} 

para

if true then 
begin
  result := foo; 
  exit; 
end;

es posible que simplemente te canses de ver que en la parte superior de cada una de tus funciones y prefieres

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

y simplemente evite exit por completo.

    
respondido por el Peter Turner 07.07.2017 - 18:46
1

Estoy de acuerdo con la siguiente declaración:

  

Soy personalmente un fan de las cláusulas de guardia   (el segundo ejemplo) ya que reduce la   Sangría de la función. Algunas personas   no me gustan porque resulta en   múltiples puntos de retorno de la   Funciona, pero creo que está más claro.   con ellos.

Tomado de esta pregunta en stackoverflow .

    
respondido por el Toto 23.05.2017 - 14:40
1

Utilizo las devoluciones tempranas casi exclusivamente en estos días, hasta un extremo. Escribo esto

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

como

self = [super init];
if (!self)
    return;

// your code here

return self;

pero realmente no importa. Si tiene más de uno o dos niveles de anidamiento en sus funciones, deben ser eliminados.

    
respondido por el Dan Rosenstark 07.07.2017 - 18:48
1

Preferiría escribir:

if(someCondition)
{
    SomeFunction();
}
    
respondido por el Josh K 07.07.2017 - 18:49
1

Al igual que usted, normalmente escribo el primero, pero prefiero el último. Si tengo muchos cheques anidados normalmente refactorizo el segundo método.

No me gusta cómo el manejo de errores se aleja del cheque.

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

Prefiero esto:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something
    
respondido por el jolt 07.07.2017 - 18:50
0

Las condiciones en la parte superior se denominan "condiciones previas". Al poner if(!precond) return; , estás visualizando visualmente todas las condiciones previas.

El uso del bloque grande "if-else" puede aumentar la sobrecarga de sangría (olvidé la cita sobre sangrías de 3 niveles).

    
respondido por el Ming-Tang 03.08.2016 - 04:41
-1

Prefiero mantener las declaraciones pequeñas.

Entonces, eligiendo entre:

if condition:
   line1
   line2
    ...
   line-n

y

if not condition: return

line1
line2
 ...
line-n

Elegiría lo que describiste como "regreso anticipado".

Eso sí, no me importan las devoluciones tempranas o lo que sea, simplemente me gusta simplificar el código, acortar los cuerpos de las declaraciones if, etc.

Los if's y for's and while de

son horribles , evítalos a toda costa.

    
respondido por el hasen 07.07.2017 - 18:50
-2

Como dicen otros, depende. Para pequeñas funciones que devuelven valores, puedo codificar las primeras devoluciones. Pero para funciones importantes, me gusta tener siempre un lugar en el código donde sé que puedo poner algo que se ejecutará antes de que vuelva.

    
respondido por el Mike Dunlavey 11.11.2010 - 20:37
-2

Practico a prueba de fallas en el nivel de la función. Mantiene el código consistente y limpio (para mí y para aquellos con quienes he trabajado). Por eso siempre vuelvo temprano.

Para algunas de las condiciones comprobadas con frecuencia, puede implementar aspectos para esas comprobaciones si usa AOP.

    
respondido por el Steven Evers 11.11.2010 - 20:38

Lea otras preguntas en las etiquetas

Comentarios Recientes

Si necesita regresar y establecer un valor opcional (el valor predeterminado) de una función, debe escribir en el hilo de llamada (excepto durante interrupciones no válidas, que se evitan enviando una excepción a el hilo de llamada). Al igual que LINQ, las instrucciones LINQ se ejecutan dentro de un bloque o dentro de una instrucción if. Sin embargo, LINQ tiene su propia semántica para múltiples declaraciones dentro de una declaración if, que se definen dentro de un bloque (principal) y no se pueden anidar dentro... Lee mas