¿Alguna vez está bien tener una declaración catch vacía?

58

Lo pensé y no pude encontrar un ejemplo. ¿Por qué alguien querría atrapar una excepción y no hacer nada al respecto? ¿Puede dar un ejemplo? Quizás es algo que nunca se debe hacer.

ysolik
fuente
¿Qué pasa finalmente?
Chris
2
por cierto, esto se preguntó en SO el año pasado: stackoverflow.com/questions/1234343/…
warren
17
al menos debería contener un comentario por qué está vacío.
peterchen

Respuestas:

77

Lo hago todo el tiempo con cosas como errores de conversión en D:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

Dicho esto, creo que todos los bloques de captura deben tener algo , incluso si ese algo es solo un comentario que explica por qué ignora la excepción.

Editar: También quiero enfatizar que, si va a hacer algo como esto, debe tener cuidado de detectar solo la excepción específica que desea ignorar. Hacer algo simple catch {}es casi siempre algo malo.

dsimcha
fuente
15
+1 para comentario explicativo.
Michael K
Algunos IDE le permiten especificar que un nombre en particular para la excepción (generalmente "ignorar") indica que lo está ignorando a propósito, lo que suprime la advertencia que de otro modo mostraría; Esto toma el lugar del comentario.
Alex Feinman el
No estoy familiarizado con D, pero supongo que es posible hacer algo como bool TryParse (línea de cadena, out double valToParse), que puede ser más limpio.
ysolik
44
Wow, alguien que realmente usa D! :-P
James McNellis
44
Como dice @Michael, no está completamente vacío ya que tienes el comentario allí; debería ser obligatorio agregar un comentario "esta captura se dejó en blanco intencionalmente" si no hay una declaración
STW
50

Un ejemplo en el que creo que está bien tragar la excepción sin hacer nada, incluso registrar la excepción, está dentro del código de registro en sí.

Si trató de registrar algo y obtuvo una excepción, no hay mucho que pueda hacer al respecto:

  • no puedes iniciar sesión, por supuesto;
  • es posible que pueda recurrir a un mecanismo de registro de reserva, pero la mayoría de las aplicaciones no son tan sofisticadas, y para aquellas que son lo suficientemente sofisticadas, con el mecanismo de registro de reserva surgirá el mismo problema de diseño;
  • falla rápida: será una solución demasiado radical para la mayoría de las aplicaciones;
  • lanzar una excepción hará poco bien, ya que terminará en una declaración de captura en la pila que casi seguramente intentará registrarlo ...

Eso te deja con la opción "menos malvada" de tragarlo silenciosamente, lo que permite que la aplicación siga realizando su trabajo principal, pero compromete la capacidad de solucionarlo.

kdubinets
fuente
10
gran punto depurar el código de depuración siempre es un desafío.
DevSolo el
77
Esto, un millón de veces. Especialmente cuando considera que si está iniciando sesión, puede estar en un estado terrible; podría quedarse sin memoria, podría fallar, podría ser cualquier cosa. En este punto, cuando está registrando un error, y ESO falla, lo único responsable es no hacer nada; no tienes garantía de que cualquier cosa que intentes no empeore las cosas.
GWLlosa
Gran punto En mi código de registro, también me gusta agregar InnerMessage para la excepción. Muchas veces esto es nulo, por lo que intentar agregarlo haría explotar el registro en sí.
Tangurena
@Tangurena Si a menudo es nulo, maneje eso, no sople. En C # a menudo guardo esas cosas por ?? "";
Loren Pechtel
8

Si de todos modos una operación que es opcional genera una excepción, es posible que no haya nada que hacer. Puede que no sea útil registrarlo. En ese caso, es posible que desee atraparlo y no hacer nada.

Si está haciendo esto, incluya un comentario. Siempre comente cualquier cosa que parezca un error (caída en una switchdeclaración, catchbloque vacío , asignación en una condición).

Podría argumentar que este no es un uso adecuado de las excepciones, pero eso es para otra pregunta.

David Thornley
fuente
6

Los catchbloques vacíos son un olor a código en la mayoría de los idiomas. La idea principal es usar excepciones para situaciones excepcionales y no usarlas para el control lógico. Todas las excepciones deben manejarse en algún lugar.

Como consecuencia de adoptar este enfoque, cuando programa una capa de aplicación en particular, tiene varias opciones:

  • no hagas nada con la excepción. Será atrapado y manejado por una capa diferente
  • atraparlo y realizar la acción correctiva.
  • atraparlo, hacer algo, pero volver a tirarlo para que otra capa lo maneje

Esto realmente no deja espacio para nada, catchbloques vacíos .

EDITADO PARA AGREGAR : suponga que está programando en un lenguaje donde lanzar excepciones es la forma normal de controlar la lógica del programa (una de las alternativas a goto). Entonces, un catchbloque vacío en un programa escrito en este idioma es muy parecido a un elsebloque vacío en un idioma tradicional (o ningún elsebloque en absoluto). Sin embargo, creo que este no es el estilo de programación recomendado por las comunidades de desarrollo C #, Java o C ++.

azheglov
fuente
Creo que usar excepciones como control lógico se ha vuelto bastante común, por lo que me encuentro con bloques de captura vacíos o casi vacíos con bastante frecuencia.
cuál es el
+1 para reconocer que un bloque de captura vacío podría indicar el uso de excepciones para el control de flujo.
John M Gant
El uso de excepciones para la lógica de programación generalmente se considera una mala práctica. Pero, sorprendentemente (mi argumento original en contra de las excepciones) las excepciones no incurren en un impacto notable en el rendimiento. Ver codeproject.com/KB/exception/ExceptionPerformance.aspx .
Evan Plaice el
6

En algunos casos, Java requiere que maneje una excepción que, bajo ninguna circunstancia, puede suceder. Considera este código:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Cada implementación de la plataforma Java es necesaria para admitir los siguientes conjuntos de caracteres estándar.

...

UTF-8

Sin romper su plataforma Java, simplemente no hay forma de causar esta excepción. Entonces, ¿por qué molestarse en manejarlo? Pero no puede simplemente omitir la cláusula try-catch-clause.

usuario281377
fuente
1
En casos como este, me vería tentado a agregar throw new Error(e)solo para estar absolutamente seguro de que no me perdí nada (o reportar una plataforma realmente rota).
Halle Knast
@HalleKnast Sí. Esto se ha discutido en detalle aquí: softwareengineering.stackexchange.com/questions/122233/…
user281377
5

Como regla general, no. Desea lanzar la excepción en la pila o registrar lo que sucedió.

Sin embargo, a menudo hago esto cuando estoy analizando cadenas y convirtiendo a números (especialmente en proyectos de mapeo que son de una sola vez y tiran a la basura).

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;
Fil
fuente
3

En algunos casos, la excepción no es en absoluto excepcional; de hecho, se espera. Considere este ejemplo:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

Como no hay un método isAlive () en java.lang.Process (), la única forma de verificar si todavía está vivo es llamar a exitValue (), que arroja una IllegalThreadStateException si el proceso aún se está ejecutando.

usuario281377
fuente
2

En mi humilde experiencia, rara vez es esto bueno. Sin embargo, su kilometraje puede variar.

Casi cada vez que he visto esto en nuestra base de código, se debe a que he rastreado un error que la excepción comido ha ocultado. En otras palabras, al no proporcionar ningún código, la aplicación no tiene forma de indicar un error o realizar otra acción.

A veces, en las capas de API, por ejemplo, es posible que no desee o no pueda dejar pasar excepciones, por lo que en ese caso, tiendo a tratar de atrapar los más genéricos y luego registrarlos. Una vez más, al menos para darle una oportunidad a una sesión de depuración / diagnóstico.

Por lo general, si debe estar vacío, lo menos que hago es poner una afirmación de algún tipo, por lo que al menos algo sucede durante una sesión de depuración. Dicho esto, si no puedo manejarlo, tiendo a omitirlos por completo.

DevSolo
fuente
2

En mi opinión, hay un lugar donde las declaraciones catch vacías están bien. En casos de prueba donde espera una excepción:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}
Victor Hurdugaci
fuente
+1, odio hacer esto pero funciona en JUnit
sal
@sal: ¿qué pasa con @Test (esperado = Exception.class)?
ysolik
@ysolik, mala costumbre
sal
1
@sal: ¿Por qué es un mal hábito?
Adam Lear
ummmm. Hay maneras de hacer esto con marcos de prueba de unidad. Ex. NUnit. Hay ciertos casos en los que probar que algo predeciblemente falla puede ser útil. Aunque, nunca lo haría en código de producción.
Evan Plaice el
2

Creo que hay ciertos casos en los que se justifica tener una cláusula catch vacía: estos son casos en los que desea que el código continúe si una operación en particular falla. Sin embargo, creo que este patrón se puede usar en exceso fácilmente, por lo que cada uso debe examinarse detenidamente para asegurarse de que no haya una mejor manera de manejarlo. En particular, esto nunca debe hacerse si deja el programa en un estado inconsistente o si podría provocar que otra parte del código falle más adelante.

o. nate
fuente
1

No creo en no tener algún nivel de alerta cuando ocurre una excepción. ¿No debería uno de los objetivos de la programación ser mejorar el código? Si se produce una excepción el 10% del tiempo y nunca se sabe, la excepción siempre será tan mala o peor.

Sin embargo, veo el otro lado, que si la excepción no causa un daño real en el procesamiento del código, entonces ¿por qué exponer al usuario? La solución que suelo implementar es que mi clase de registrador lo registre (es decir, en cada clase que escribo en el trabajo).

Si es una excepción benigna, entonces hago una llamada al método .Debug () de mi Logger; de lo contrario, Logger.Error () (y (quizás) throw).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

Por cierto, en mi implementación de mi registrador, hacer una llamada al método .Debug () solo lo registrará si mi archivo App.Config especifica que el nivel de registro de ejecución del programa está en modo de depuración.

Tim Claason
fuente
¿Qué pasa si _log.Debug arroja una excepción (por cualquier razón)?
JohnL
Entonces .Debug () come la excepción y nunca lo sé. Pero me siento mucho más seguro sobre mi registrador que sobre el código que a menudo arroja excepciones. Si me preocupara eso, supongo que podría implementar algún tipo de patrón que vigile la entrada en el bloque de captura y lo guarde para más adelante. En cualquier caso - punto tomado
Tim Claason
1

La comprobación de existencia es un buen caso de uso:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

Otro caso es cuando finallyse usa una cláusula:

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

Existe una propuesta para permitir la omisión del enlace de captura en ECMAScript que se refiere a este y otros casos de uso similares.

Referencias

Paul Sweatte
fuente
Otro ejemplo: en nodejs, el método fs.exists (que devuelve verdadero o falso) está en desuso ( nodejs.org/dist/latest-v10.x/docs/api/… ) a favor de fs.access que arroja una excepción en caso no existe un archivo. Si uno quiere hacer algo solo si el archivo existe, la cláusula catch es totalmente innecesaria.
systemovich
0

La única vez que realmente necesité hacer algo como esto fue con una clase de registro para una aplicación de consola. Hubo un controlador de captura global de nivel superior que emitió el error a STDOUT, registró el error en un archivo y luego cerró la aplicación con un código de error> 0.

El problema aquí era que a veces, el registro en el archivo fallaba. Los permisos de directorio le impidieron escribir el archivo, el archivo estaba bloqueado exclusivamente, lo que sea. Si eso sucediera, no podría manejar la excepción de la misma manera que lo hice en otros lugares (capturar, registrar los detalles relevantes , ajustar un tipo de excepción mejor, etc.) porque registrar los detalles de la excepción hizo que el registrador realizara las mismas acciones ( tratando de escribir en un archivo) que ya había fallado. Cuando fallaron de nuevo ... Ya ves a dónde voy. Se mete en un bucle infinito.

Si suelta algunos de los bloques try {}, puede terminar con la excepción que aparece en un cuadro de mensaje (no lo que desea para una aplicación de configuración silenciosa). Entonces, en ese método que escribe en el archivo de registro, tengo un controlador de captura vacío. Esto no entierra el error ya que aún obtienes el código de error> 0, solo detiene el bucle infinito y permite que la aplicación salga con gracia.

Por supuesto, hay un comentario que explica por qué está vacío.

(Iba a publicar esto antes, solo tenía miedo de quedar en el olvido. Sin embargo, como no soy el único ...)

JohnL
fuente
0

Está bien en una situación en la que se debe ejecutar alguna secuencia sin importar con la pieza más crítica ubicada al final.

Por ejemplo. En la comunicación de control de movimiento del host al controlador. En situaciones de emergencia, hay una serie de pasos de preparación para abortar el movimiento, detener la generación de trayectoria, desacelerar los motores, habilitar los descansos, desactivar el amplificador y después de esto debe apagar la alimentación del bus. Todo debe suceder secuencialmente y si uno de los pasos falla, el resto de los pasos deben ejecutarse de todos modos incluso con el riesgo aceptado de dañar el hardware. Digamos que incluso si todo lo anterior falla, la potencia del bus de muerte debe suceder de todos modos.

usuario7071
fuente
Eso no significa que no hagas nada, solo que lo que haces no aborta el flujo. Capture sus excepciones, guárdelas en la memoria (sin demora de escritura en disco), luego elimine el poder en una declaración final. Luego haga lo que quiera con la información guardada.
Loren Pechtel el
0

Cerrar recursos del sistema con gracia para recuperarse de un error

Por ejemplo. Si está ejecutando un servicio en segundo plano que no proporciona comentarios al usuario, es posible que no desee enviar una indicación del error al usuario, pero necesita cerrar los recursos que se utilizan de manera elegante.

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

Idealmente, usaría la captura en modo de depuración para detectar errores e imprimirlos en la consola o en un archivo de registro para probar. En un entorno de producción, generalmente se espera que los servicios en segundo plano no interrumpan al usuario cuando se produce un error, por lo que se debe suprimir la salida del error.

Evan Plaice
fuente