Ci ho pensato e non ho potuto trovare un esempio. Perché qualcuno dovrebbe prendere un'eccezione e non fare nulla al riguardo? Puoi fare un esempio? Forse è solo qualcosa che non dovrebbe mai essere fatto.
Lo faccio sempre con cose come gli errori di conversione in 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.
}
Detto questo, penso che tutti i blocchi catch dovrebbero avere qualcosa in essi, anche se quel qualcosa è solo un commento che spiega perché stai ignorando l'eccezione.
Modifica: voglio anche sottolineare che, se hai intenzione di fare qualcosa del genere, dovresti fare attenzione a catturare solo l'eccezione specifica che vuoi ignorare. Fare un semplice vecchio catch {}
è quasi sempre una brutta cosa.
Un esempio in cui ritengo sia giusto ingoiare un'eccezione senza fare nulla, anche la registrazione dell'eccezione è all'interno del codice di registrazione stesso.
Se hai provato a registrare qualcosa e hai ottenuto delle eccezioni, non c'è molto che puoi fare al riguardo:
Questo ti lascia con l'opzione "Il meno male" di ingoiarla tranquillamente, permettendo all'applicazione di continuare a svolgere il suo compito principale, ma compromettendo la capacità di risolverlo.
Se un'eccezione viene lanciata da un'operazione comunque facoltativa, potrebbe non esserci nulla da fare. Potrebbe non essere utile registrarlo. In tal caso, potresti semplicemente prenderlo e non fare nulla.
Se lo fai, includi un commento. Commenta sempre qualsiasi cosa che assomigli ad un bug (ricaduta in un'istruzione switch
, vuoto catch
blocco, assegnazione in una condizione).
Potresti sostenere che non si tratta di un uso corretto delle eccezioni, ma si tratta di un'altra domanda.
I blocchi catch
vuoti sono un odore di codice nella maggior parte delle lingue. L'idea principale è usare le eccezioni per situazioni eccezionali e non usarle per il controllo logico. Tutte le eccezioni devono essere gestite da qualche parte.
Come conseguenza dell'adozione di questo approccio, quando si programma un particolare livello di applicazione, sono disponibili diverse opzioni:
Questo in realtà non lascia spazio a nulla, blocchi catch
vuoti.
MODIFICA DA AGGIUNGERE : supponiamo di stare programmando in una lingua in cui lanciare le eccezioni è il modo normale di controllare la logica del programma (una delle alternative a goto
). Quindi un blocco catch
vuoto in un programma scritto in questa lingua è molto simile a un blocco else
vuoto in un linguaggio tradizionale (o nessun blocco else
). Tuttavia, credo che questo non sia lo stile di programmazione raccomandato dalle comunità di sviluppo C #, Java o C ++.
In alcuni casi, Java richiede di gestire un'eccezione che, in nessun caso, può mai accadere. Considera questo codice:
try {
bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}
Every implementation of the Java platform is required to support the following standard charsets.
...
UTF-8
Senza rompere la tua piattaforma Java, non c'è semplicemente modo di causare questa eccezione. Quindi perché preoccuparsi di gestirlo? Ma non puoi semplicemente omettere la clausola try-catch-.
Come regola generale, no. O vuoi buttare l'eccezione in cima allo stack o registrare quello che è successo.
Tuttavia, lo faccio spesso quando sto analizzando le stringhe e convertendo in numeri (specialmente nei progetti di mappatura che sono di una sola volta: una varietà usa e getta).
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;
Nella mia umile esperienza, raramente è questa buona cosa. Tuttavia, il chilometraggio può variare.
Quasi ogni volta che ho visto questo nella nostra base di codice, è stato perché ho rintracciato un bug che l'eccezione mangiata ha nascosto. In altre parole, non fornendo alcun codice, l'applicazione non ha modo di indicare un errore o eseguire un'altra azione.
A volte, nei livelli API, ad esempio, potresti non volere o essere in grado di far passare le eccezioni, quindi in tal caso, tendo a cercare di individuare le eccezioni più generiche, quindi registrare quelle. Ancora una volta, per dare almeno una sessione di debug / diagnosi una possibilità.
Tipicamente, se deve essere vuoto, il minimo che faccio è mettere un'asserzione di qualche tipo, quindi almeno qualcosa accade durante una sessione di debug. Detto questo, se non riesco a gestirlo, tendo a ometterli completamente.
IMO c'è un posto in cui le dichiarazioni di cattura vuote sono OK. Nei casi di test in cui si prevede un'eccezione:
try
{
DoSomething(); //This should throw exception if everything works well
Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
//Expected to get here
}
Credo ci siano alcuni casi in cui è giustificato avere una clausola di catch vuota: si tratta di casi in cui si desidera che il codice continui a funzionare solo se una determinata operazione fallisce. Tuttavia, penso che questo modello possa essere facilmente abusato, quindi ogni uso dovrebbe essere esaminato attentamente per assicurarsi che non ci sia un modo migliore per gestirlo. In particolare, questo non dovrebbe mai essere fatto se lascia il programma in uno stato incoerente o se potrebbe portare a qualche altra parte del codice fallendo in seguito.
In alcuni casi, l'eccezione non è affatto eccezionale; infatti, è previsto. Considera questo esempio:
/* 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 */ }
Poiché non esiste un metodo isAlive () in java.lang.Process (), l'unico modo per verificare se è ancora vivo è chiamare exitValue (), che genera un IllegalThreadStateException se il processo è ancora in esecuzione.
Non credo di non avere un certo livello di allerta quando si verifica un'eccezione - uno degli obiettivi della programmazione non dovrebbe essere quello di migliorare il codice? Se si verifica un'eccezione il 10% delle volte e non si è mai a conoscenza, l'eccezione sarà sempre quella pessima o peggio.
Tuttavia, vedo l'altro lato, che se l'eccezione non ha alcun danno reale nell'elaborazione del codice, allora perché esporre l'utente ad esso. La soluzione che di solito implemento è averla registrata dalla mia classe di logger (che è in ogni singola classe che scrivo sul lavoro).
Se si tratta di un'eccezione benigna, faccio una chiamata al metodo .Debug () del mio Logger; altrimenti, Logger.Error () (e (forse) throw).
try
{
doWork();
}
catch(BenignException x)
{
_log.Debug("Error doing work: " + x.Message);
}
A proposito, nella mia implementazione del mio logger, effettuare una chiamata al metodo .Debug () lo registrerà solo se il mio file App.Config specifica che il livello di log dell'esecuzione del programma è in modalità Debug.
L'unica volta che ho davvero avuto bisogno di fare qualcosa di simile era con una classe di registrazione per un'app console. C'era un gestore globale di cattura di livello superiore che ha emesso l'errore in STDOUT, ha registrato l'errore in un file, quindi ha chiuso l'applicazione con un codice di errore > 0.
Il problema qui era che a volte la registrazione del file falliva. Le autorizzazioni della directory ti impedivano di scrivere il file, il file era bloccato in modo esclusivo, qualunque cosa. Se ciò fosse accaduto, non avrei potuto gestire l'eccezione nello stesso modo in cui avevo altrove (catch, registra i dettagli rilevanti , racchiudi un tipo di eccezione migliore, ecc.) Perché la registrazione dei dettagli delle eccezioni ha causato il logger passare attraverso le stesse azioni (cercando di scrivere su un file) che aveva già fallito. Quando hanno fallito di nuovo ... Vedi dove sto andando. Entra in un ciclo infinito.
Se si rilasciano alcuni dei blocchi try {}, si può finire con l'eccezione che appare in una finestra di messaggio (non ciò che si desidera per un'app di configurazione silenziosa). Quindi, in quel metodo che scrive nel file di log, ho un gestore catch vuoto. Questo non seppellisce l'errore poiché ottieni ancora il codice di errore > 0, interrompe semplicemente il ciclo infinito e consente all'app di uscire con garbo.
Ovviamente c'è un commento, che spiega perché è vuoto.
(stavo per postarlo prima, avevo solo paura di essere fiammeggiato nel dimenticatoio, visto che non sono l'unico, anche se ...)
È ok in situazioni in cui è necessario eseguire alcune sequenze, indipendentemente dal pezzo più critico posizionato alla fine.
Ad esempio. Nella comunicazione di controllo del movimento dell'host al controller. In situazioni di emergenza vi è una serie di fasi di preparazione per interrompere il movimento, fermare la generazione della traiettoria, decelerare i motori, abilitare le interruzioni, disabilitare l'amplificatore e dopo questo è necessario interrompere la potenza del bus. Tutto deve succedere in sequenza e se uno dei passaggi fallisce, il resto dei passi deve essere eseguito comunque anche con il rischio accettato di danneggiare l'hardware. Dì anche se tutto questo non funziona, la potenza del kill bus deve comunque accadere.
Chiusura delle risorse di sistema con garbo per recuperare da un errore
Per esempio. Se stai eseguendo un servizio in background che non fornisce feedback all'utente, potresti non voler inviare un'indicazione dell'errore all'utente ma do devi chiudere le risorse essere usato in modo aggraziato.
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
Idealmente, dovresti usare il catch in modalità debug per rilevare gli errori e stamparli sulla console o su un file di log per il test. In un ambiente di produzione, i servizi in background generalmente non interrompono l'utente quando viene generato un errore, quindi l'output dell'errore deve essere soppresso.
Il controllo dell'esistenza è un buon caso d'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);
}
Un altro caso è quando viene utilizzata una clausola finally
:
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();
Esiste una proposta per consentire l' omissione del binding catch in ECMAScript che si riferisce a questo e casi d'uso simili.
Riferimenti
Leggi altre domande sui tag exceptions