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.
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.
Respuestas:
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.
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:
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.
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 switch
declaración, catch
bloque 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.
Los catch
bloques 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:
Esto realmente no deja espacio para nada, catch
bloques 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 catch
bloque vacío en un programa escrito en este idioma es muy parecido a un else
bloque vacío en un idioma tradicional (o ningún else
bloque en absoluto). Sin embargo, creo que este no es el estilo de programación recomendado por las comunidades de desarrollo C #, Java o C ++.
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.
throw new Error(e)
solo para estar absolutamente seguro de que no me perdí nada (o reportar una plataforma realmente rota).
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;
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.
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.
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
}
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.
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.
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 finally
se 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
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 ...)
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.
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 sí 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.