¿Por qué los bloques vacíos son una mala idea? [cerrado]

192

Acabo de ver una pregunta sobre try-catch , ¿qué personas (incluido Jon Skeet) dicen que los bloques de captura vacíos son una muy mala idea? ¿Por qué esto? ¿No hay una situación en la que una captura vacía no sea una decisión de diseño incorrecta?

Quiero decir, por ejemplo, a veces quieres obtener información adicional de algún lugar (servicio web, base de datos) y realmente no te importa si obtendrás esta información o no. Así que intenta obtenerlo, y si algo sucede, está bien, solo agregaré un "catch (Excepción ignorada) {}" y eso es todo

Samuel Carrijo
fuente
53
Escribiría un comentario explicando por qué debería estar vacío.
Mehrdad Afshari
55
... o al menos registrar que fue capturado.
mate b
2
@ocdecio: evítelo para el código de limpieza , no lo evite en general.
John Saunders
1
@ocdecio: Otro caso para evitar el uso de try..catch es cuando detecta excepciones para tipos conocidos de fallas. Por ejemplo, excepciones de conversión numérica
MPritchard
1
@ocdecio - probar ... finalmente es mejor que intentar ... captura vacía porque el error continúa en la pila - para que el marco lo maneje o para matar el programa y mostrarlo al usuario - ambos resultados son mejores que un " fracaso silencioso ".
Nate

Respuestas:

297

Por lo general, el try-catch vacío es una mala idea porque está tragando silenciosamente una condición de error y luego continúa la ejecución. Ocasionalmente, esto puede ser lo correcto, pero a menudo es una señal de que un desarrollador vio una excepción, no sabía qué hacer al respecto y, por lo tanto, usó una captura vacía para silenciar el problema.

Es el equivalente de programación de poner cinta negra sobre una luz de advertencia del motor.

Creo que la forma en que maneja las excepciones depende de la capa de software en la que esté trabajando: Excepciones en la selva tropical .

Ned Batchelder
fuente
44
En esas circunstancias realmente raras en las que realmente no necesito la excepción y el registro no está disponible por alguna razón, me aseguro de comentar que es intencional y por qué no es necesario, y reitero que aún preferiría registrarlo si era factible en ese entorno. Básicamente, hago el comentario MÁS trabajo de lo que hubiera sido el registro si hubiera sido una opción en esa circunstancia. Afortunadamente, tengo solo 2 clientes para quienes esto es un problema, y ​​es solo cuando envío correos electrónicos no críticos desde sus sitios web.
John Rudy el
37
También me encanta la analogía, y como todas las reglas, hay excepciones. Por ejemplo, está perfectamente bien usar cinta negra sobre el reloj parpadeante de su videograbadora si nunca tiene la intención de hacer grabaciones cronometradas (para todos los viejos que recuerdan lo que es una videograbadora).
Michael Burr
3
Al igual que cualquier desviación de la práctica aceptada, hazlo si es apropiado y documentalo para que el próximo tipo sepa lo que hiciste y por qué.
David Thornley
55
@ Jason: como mínimo, debe incluir un comentario detallado que explique por qué está silenciando las excepciones.
Ned Batchelder
1
Esta respuesta supone dos cosas: 1. Que la excepción que se está lanzando es realmente significativa y que quien escribió el código que arroja sabía lo que estaba haciendo; 2. Que la excepción es de hecho una "condición de error" y no se abusa como flujo de control (aunque este es un caso más específico de mi primer punto).
Boris B.
38

Son una mala idea en general porque es una condición verdaderamente rara en la que una falla (condición excepcional, más genérica) se cumple adecuadamente sin respuesta alguna. Además de eso, los catchbloques vacíos son una herramienta común utilizada por las personas que usan el motor de excepción para la verificación de errores que deberían estar haciendo de manera preventiva.

Decir que siempre es malo no es cierto ... eso es verdad de muy poco. Puede haber circunstancias en las que no le importa que haya un error o que la presencia del error de alguna manera indique que no puede hacer nada al respecto de todos modos (por ejemplo, al escribir un error anterior en un archivo de registro de texto y obtienes un IOException, lo que significa que no podrías escribir el nuevo error de todos modos).

Adam Robinson
fuente
11

No exageraría las cosas hasta decir que quien usa bloques de captura vacíos es un mal programador y no sabe lo que está haciendo ...

Utilizo bloques vacíos si es necesario. A veces, el programador de la biblioteca que estoy consumiendo no sabe lo que está haciendo y arroja excepciones incluso en situaciones en las que nadie lo necesita.

Por ejemplo, considere alguna biblioteca de servidor http, no me importaría menos si el servidor arroja una excepción porque el cliente se ha desconectado y index.htmlno se pudo enviar.

lubos hasko
fuente
66
Ciertamente estás sobregeneralizando. El hecho de que no lo necesite no significa que nadie lo necesite. Incluso si no se puede hacer absolutamente nada en respuesta, hay alguien en algún lugar con el requisito de recopilar estadísticas sobre conexiones abandonadas. Por lo tanto, es bastante grosero suponer que "el programador de la biblioteca no sabe lo que está haciendo".
Ben Voigt
8

Hay casos raros en los que puede justificarse. En Python a menudo ves este tipo de construcción:

try:
    result = foo()
except ValueError:
    result = None

Por lo tanto, podría estar bien (dependiendo de su aplicación) hacer:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

En un proyecto reciente de .NET, tuve que escribir código para enumerar las DLL de complementos para encontrar clases que implementaran una interfaz determinada. El bit de código relevante (en VB.NET, lo siento) es:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Aunque incluso en este caso, admitiría que registrar el fallo en algún lugar probablemente sería una mejora.

Daniel Pryden
fuente
Me referí a log4net como una de esas instancias antes de ver tu respuesta.
Scott Lawrence
7

Los bloques de captura vacíos generalmente se colocan porque el codificador realmente no sabe lo que está haciendo. En mi organización, un bloque de captura vacío debe incluir un comentario sobre por qué no hacer nada con la excepción es una buena idea.

En una nota relacionada, la mayoría de la gente no sabe que un bloque try {} puede seguirse con un catch {} o finalmente {}, solo se requiere uno.

Justin
fuente
1
+1
Destacaría
Sin embargo, ese segundo párrafo puede depender del idioma, ¿verdad?
David Z
3
a menos por supuesto que estamos hablando IE6 o IE7 ;-) ... donde la captura SE requiere o los finalmente no se ejecuta. (esto se solucionó en IE8 por cierto)
scunliffe
Muy cierto. Podría valer la pena destacar los idiomas. Creo que .net está bien
MPritchard 05 de
7

Solo se deben lanzar excepciones si realmente hay una excepción: algo que está sucediendo más allá de la norma. Un bloque de captura vacío básicamente dice "algo malo está sucediendo, pero simplemente no me importa". Esta es una mala idea.

Si no desea manejar la excepción, deje que se propague hacia arriba hasta que llegue a algún código que pueda manejarlo. Si nada puede manejar la excepción, debería quitar la aplicación.

Reed Copsey
fuente
3
A veces sabes que no va a afectar nada. Luego, adelante y hazlo, y coméntalo para que el siguiente tipo no solo piense que lo arruinaste porque eres incompetente.
David Thornley
Sí, hay un momento y un lugar para todo. Sin embargo, en general, creo que un bloque catch vacío que es bueno es la excepción a la regla: es un caso raro en el que desea ignorar realmente una excepción (por lo general, en mi opinión, es el resultado de un mal uso de las excepciones).
Reed Copsey
2
@David Thornley: ¿Cómo sabes que no afectará nada si al menos no inspeccionas la excepción para determinar si es un error esperado y sin sentido o uno que debería propagarse hacia arriba?
Dave Sherohman el
2
@Dave: Si bien catch (Exception) {}es una mala idea, catch (SpecificExceptionType) {}podría estar perfectamente bien. El programador inspeccionó la excepción, utilizando la información de tipo en la cláusula catch.
Ben Voigt
7

Creo que está bien si detecta un tipo de excepción particular del cual sabe que solo se generará por una razón en particular , y espera esa excepción y realmente no necesita hacer nada al respecto.

Pero incluso en ese caso, un mensaje de depuración podría estar en orden.

balpha
fuente
5

Por Josh Bloch - Artículo 65: No ignore las excepciones de Java efectivo :

  1. Un bloque de captura vacío anula el propósito de las excepciones.
  2. Como mínimo, el bloque catch debe contener un comentario que explique por qué es apropiado ignorar la excepción.
KrishPrabakar
fuente
3

Un bloque de captura vacío básicamente dice "No quiero saber qué errores se lanzan, solo voy a ignorarlos".

Es similar a los VB6 On Error Resume Next, excepto que se omitirá cualquier cosa en el bloque de prueba después de lanzar la excepción.

Lo que no ayuda cuando algo se rompe.

Powerlord
fuente
De hecho, diría que "Al reanudar el error a continuación" es peor, solo intentará la siguiente línea independientemente.
Saltará
Buen punto. Probablemente debería notar eso en mi respuesta.
Powerlord
1
Esto no es del todo cierto, si usted tiene una oportunidad vacío ... a la pelota con un tipo específico de excepción, entonces simplemente ignorará este tipo de excepción, y que podría ser legítimo ...
Meta-Knight
Pero si sabe que se está lanzando un tipo particular de excepción, parece que podría tener suficiente información para escribir su lógica de una manera que evite el uso de try-catch para lidiar con la situación.
Scott Lawrence
@Scott: Ciertos lenguajes (es decir, Java) tienen excepciones comprobadas ... excepciones para las que debe escribir bloques catch.
Powerlord
3

Esto va de la mano con "No use excepciones para controlar el flujo del programa" y "Solo use excepciones para circunstancias excepcionales". Si se hacen, las excepciones solo deberían ocurrir cuando hay un problema. Y si hay un problema, no querrás fallar en silencio. En las raras anomalías donde no es necesario manejar el problema, al menos debe registrar la excepción, en caso de que la anomalía deje de ser una anomalía. Lo único peor que fallar es fallar en silencio.

Imaginista
fuente
2

Creo que un bloque catch completamente vacío es una mala idea porque no hay forma de inferir que ignorar la excepción fue el comportamiento previsto del código. No es necesariamente malo tragar una excepción y devolver falso o nulo o algún otro valor en algunos casos. El marco .net tiene muchos métodos de "prueba" que se comportan de esta manera. Como regla general si se traga una excepción, agregue un comentario y una declaración de registro si la aplicación admite el registro.

complexcipher
fuente
1

Porque si se lanza una excepción , nunca la verás (fallar en silencio es la peor opción posible) obtendrás un comportamiento erróneo y no tendrás idea de dónde está sucediendo. ¡Al menos pon un mensaje de registro allí! ¡Incluso si es algo que 'nunca puede suceder'!

Nate
fuente
1

Los bloques de captura vacíos son una indicación de que un programador no sabe qué hacer con una excepción. Están suprimiendo la excepción de posiblemente surgir y ser manejado correctamente por otro bloque de prueba. Siempre trate de hacer algo con la excepción que está atrapando.

Dan
fuente
1

Lo más molesto con las declaraciones catch vacías es cuando algún otro programador lo hizo. Lo que quiero decir es que cuando necesita depurar el código de otra persona, cualquier declaración catch vacía hace que dicha tarea sea más difícil de lo que debe ser. En mi humilde opinión, las declaraciones catch siempre deben mostrar algún tipo de mensaje de error, incluso si el error no se maneja, al menos debería detectarlo (alt. Solo en modo de depuración)

AndersK
fuente
1

Probablemente nunca sea lo correcto porque estás pasando silenciosamente todas las excepciones posibles. Si hay una excepción específica que está esperando, entonces debe probarla, volver a lanzarla si no es su excepción.

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}
xanadont
fuente
1
Ese no es realmente un patrón estándar que estás recomendando a la gente que use allí. La mayoría de las personas simplemente atraparían las Excepciones de cualquier tipo que estén esperando, y no las que no son. Puedo ver algunas circunstancias en las que su enfoque sería válido, solo tenga cuidado de publicar en un foro como este donde la gente tiende a leer cosas como esta porque no entienden en primer lugar;)
MPritchard
Mi intención no era que IsKnownException () esté verificando el tipo de excepción (sí, debe hacerlo por diferentes bloques de captura), sino que está verificando si es una excepción esperada. Lo editaré para que quede más claro.
xanadont 05 de
Originalmente estaba pensando en COMExceptions. Puede probar un Código de error específico y manejar los códigos que espera. Hago esto a menudo cuando programo con ArcOjbects.
xanadont 05 de
2
-1 Tomar decisiones en el código basado en el mensaje de excepción es una muy mala idea. El mensaje exacto rara vez se documenta y puede cambiar en cualquier momento; Es un detalle de implementación. O el mensaje podría basarse en una cadena de recursos localizable. En cualquier caso, solo debe ser leído por un humano.
Wim Coenen
Eek La excepción. El mensaje puede ser localizado y, por lo tanto, no es lo que espera. Esto está mal.
Frankhommers
1

En general, solo debe detectar las excepciones que realmente puede manejar. Eso significa ser lo más específico posible al detectar excepciones. Capturar todas las excepciones rara vez es una buena idea e ignorar todas las excepciones es casi siempre una muy mala idea.

Solo puedo pensar en algunos casos en los que un bloque de captura vacío tiene un propósito significativo. Si cualquiera que sea la excepción específica, se está "manejando" simplemente volviendo a intentar la acción, no habría necesidad de hacer nada en el bloque de captura. Sin embargo, aún sería una buena idea registrar el hecho de que ocurrió la excepción.

Otro ejemplo: CLR 2.0 cambió la forma en que se tratan las excepciones no controladas en el subproceso finalizador. Antes de 2.0, el proceso podía sobrevivir a este escenario. En el CLR actual, el proceso finaliza en caso de una excepción no controlada en el subproceso finalizador.

Tenga en cuenta que solo debe implementar un finalizador si realmente lo necesita e incluso entonces debe hacer lo menos posible en el finalizador. Pero si cualquier trabajo que debe hacer su finalizador podría arrojar una excepción, debe elegir entre el menor de los dos males. ¿Desea cerrar la aplicación debido a la excepción no controlada? ¿O quieres proceder en un estado más o menos indefinido? Al menos en teoría, este último puede ser el menor de dos males en algunos casos. En esos casos, el bloque catch vacío evitaría que el proceso se terminara.

Brian Rasmussen
fuente
1
Quiero decir, por ejemplo, a veces quieres obtener información adicional de algún lugar (servicio web, base de datos) y realmente no te importa si obtendrás esta información o no. Así que intenta obtenerlo, y si algo sucede, está bien, solo agregaré un "catch (Excepción ignorada) {}" y eso es todo

Entonces, siguiendo su ejemplo, es una mala idea en ese caso porque está captando e ignorando todas las excepciones. Si solo estuvieras atrapando EInfoFromIrrelevantSourceNotAvailablee ignorando eso, estaría bien, pero no lo estás. También estás ignorando ENetworkIsDown, lo que puede o no ser importante. Estás ignorando ENetworkCardHasMeltedyEFPUHasDecidedThatOnePlusOneIsSeventeen , lo que es casi seguro, importante.

Un bloque catch vacío no es un problema si está configurado para capturar solo (e ignorar) excepciones de ciertos tipos que usted sabe que no son importantes. Las situaciones en las que es una buena idea suprimir e ignorar silenciosamente todas las excepciones, sin detenerse a examinarlas primero para ver si son esperadas / normales / irrelevantes o no, son extremadamente raras.

Dave Sherohman
fuente
1

Hay situaciones en las que podría usarlos, pero deberían ser muy poco frecuentes. Las situaciones en las que podría usar una incluyen:

  • registro de excepciones; dependiendo del contexto, es posible que desee publicar una excepción no controlada o un mensaje.

  • situaciones técnicas en bucle, como procesamiento o procesamiento de sonido o una devolución de llamada de cuadro de lista, donde el comportamiento en sí mismo demostrará el problema, lanzar una excepción solo se interpondrá en el camino, y registrar la excepción probablemente solo dará como resultado miles de mensajes "fallidos a XXX" .

  • programas que no pueden fallar, aunque al menos deberían estar registrando algo.

Para la mayoría de las aplicaciones winforms, he descubierto que es suficiente tener una única declaración de prueba para cada entrada del usuario. Utilizo los siguientes métodos: (AlertBox es solo un rápido MessageBox.Show wrapper)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Entonces cada controlador de eventos puede hacer algo como:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

o

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

Teóricamente, podría tener TryActionSilently, que podría ser mejor para realizar llamadas de modo que una excepción no genere una cantidad interminable de mensajes.

Dave Cousineau
fuente
1

Si no sabe qué hacer en el bloque catch, puede registrar esta excepción, pero no dejarla en blanco.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }
Andzej Maciusovic
fuente
¿Por qué se rechazó esto?
Vino
0

Nunca debe tener un bloque de captura vacío. Es como esconder un error que conoces. Como mínimo, debe escribir una excepción a un archivo de registro para revisar más adelante si tiene poco tiempo.

Chadit
fuente