Ci ho pensato e non ho potuto fare un esempio. Perché qualcuno dovrebbe voler prendere un'eccezione e non fare nulla al riguardo? Puoi fare un esempio? Forse è solo qualcosa che non dovrebbe mai essere fatto.
Ci ho pensato e non ho potuto fare un esempio. Perché qualcuno dovrebbe voler prendere un'eccezione e non fare nulla al riguardo? Puoi fare un esempio? Forse è solo qualcosa che non dovrebbe mai essere fatto.
Risposte:
Lo faccio sempre con cose come 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 di cattura dovrebbero avere qualcosa in loro, 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 cogliere solo l'eccezione specifica che vuoi ignorare. Fare un vecchio semplice catch {}
è quasi sempre una brutta cosa.
Un esempio in cui penso sia giusto ingoiare l'eccezione senza fare nulla, anche registrando l'eccezione, è all'interno del codice di registrazione stesso.
Se hai provato a registrare qualcosa e hai ottenuto un'eccezione, non c'è molto che puoi fare al riguardo:
Ciò ti lascia con l'opzione "meno del male" di ingoiarlo in silenzio, consentendo all'applicazione di continuare a svolgere il suo lavoro principale, ma compromettendo la capacità di risolverlo.
Se viene generata un'eccezione da un'operazione facoltativa, potrebbe non esserci nulla da fare. Potrebbe non essere utile registrarlo. In tal caso, potresti semplicemente voler prenderlo e non fare nulla.
Se lo stai facendo, includi un commento. Commenta sempre tutto ciò che sembra un bug (descrizione dettagliata di una switch
dichiarazione, catch
blocco vuoto , assegnazione in una condizione).
Potresti sostenere che questo non è un uso corretto delle eccezioni, ma è per un'altra domanda.
I catch
blocchi 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 di questo approccio, quando si programma un particolare livello di applicazione, sono disponibili diverse opzioni:
Questo non lascia spazio a catch
blocchi vuoti senza nulla .
MODIFICATO PER AGGIUNGERE : supponiamo che tu stia programmando in un linguaggio in cui lanciare eccezioni è il modo normale di controllare la logica del programma (una delle alternative a goto
). Quindi un catch
blocco vuoto in un programma scritto in questa lingua è molto simile a un else
blocco vuoto in una lingua tradizionale (o nessun else
blocco). 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) {
}
Ogni implementazione della piattaforma Java è necessaria per supportare i seguenti set di caratteri standard.
...
UTF-8
Senza rompere la tua piattaforma Java, non c'è semplicemente alcun modo per causare questa eccezione. Allora perché preoccuparsi di gestirlo? Ma non puoi semplicemente aggiungere la clausola try-catch.
throw new Error(e)
solo per essere assolutamente sicuro di non aver perso nulla (o di segnalare piattaforme effettivamente rotte).
Come regola generale, no. O vuoi gettare l'eccezione nello stack o registrare ciò che è successo.
Tuttavia, lo faccio spesso quando analizzo le stringhe e la conversione in numeri (specialmente nella mappatura di progetti che sono di uso una volta e butto via la varietà).
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;
In alcuni casi, l'eccezione non è affatto eccezionale; in effetti, è 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 attivo è chiamare exitValue (), che genera un oggetto IllegalThreadStateException se il processo è ancora in esecuzione.
Nella mia umile esperienza, raramente è una cosa positiva. Il chilometraggio può variare però.
Quasi ogni volta che l'ho visto nella nostra base di codice, è stato perché ho rintracciato un bug che l'eccezione mangiata ha nascosto. Per dirla in altro modo, non fornendo alcun codice, l'applicazione non ha modo di indicare un errore o eseguire un'altra azione.
A volte, ad esempio nei livelli API, potresti non voler o essere in grado di far passare le eccezioni, quindi in quel caso, tendo a cercare di catturare il più generico, quindi registrarle. Ancora una volta, per dare almeno una possibilità a una sessione di debug / diagnosi.
In genere, se deve essere vuoto, il minimo che faccio è 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 dove le dichiarazioni di cattura vuote sono OK. Nei casi di test in cui ti aspetti 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 che ci siano alcuni casi in cui è giustificato avere una clausola catch vuota - questi sono casi in cui si desidera che il codice continui a funzionare se una determinata operazione fallisce. Tuttavia, penso che questo modello possa essere facilmente abusato, quindi ogni utilizzo dovrebbe essere attentamente esaminato per assicurarsi che non ci sia un modo migliore per gestirlo. In particolare, ciò non dovrebbe mai essere fatto se lascia il programma in uno stato incoerente o se potrebbe portare a qualche altra parte del codice che fallisce in seguito.
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 un'eccezione si verifica il 10% delle volte e non si è mai a conoscenza di essa, allora l'eccezione sarà sempre così grave o peggiore.
Tuttavia, vedo l'altro lato, che se l'eccezione non fa alcun danno reale nell'elaborazione del codice, perché esporre l'utente ad esso. La soluzione che in genere implemento è averlo registrato dalla mia classe di logger (che è in ogni singola classe che io abbia mai scritto al lavoro).
Se è un'eccezione benigna, allora faccio una chiamata al metodo .Debug () del mio Logger; in caso contrario, Logger.Error () (e (forse) lancio).
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 del registro di esecuzione del programma è in modalità Debug.
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 finally
viene utilizzata una clausola:
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 vincolo di cattura in ECMAScript che riguarda questo e altri casi d'uso simili.
Riferimenti
L'unica volta in cui ho davvero avuto bisogno di fare qualcosa del genere era con una classe di registrazione per un'app console. Esisteva un gestore di cattura globale di livello superiore che emetteva l'errore su STDOUT, registrava l'errore su un file, quindi chiudeva l'app con un codice di errore> 0.
Il problema qui era che a volte la registrazione nel file non riusciva. I permessi della directory ti hanno impedito di scrivere il file, il file era bloccato esclusivamente, qualunque cosa. In tal caso, non potrei gestire l'eccezione nello stesso modo che avevo altrove (cattura, registra i dettagli rilevanti , avvolgi in un tipo di eccezione migliore, ecc.) Perché la registrazione dei dettagli dell'eccezione ha fatto sì che il logger eseguisse le stesse azioni ( tentando di scrivere su un file) che non era già riuscito. Quando hanno fallito di nuovo ... Vedi dove sto andando. Entra in un ciclo infinito.
Se elimini alcuni dei blocchi try {}, puoi finire con l'eccezione che appare in una finestra di messaggio (non quello che vuoi per un'app di configurazione silenziosa). Quindi in quel metodo che scrive nel file di registro, ho un gestore di cattura 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 grazia.
C'è ovviamente un commento, che spiega perché è vuoto.
(Avevo intenzione di postare questo in precedenza, avevo solo paura di farmi infiammare nell'oblio. Dato che non sono l'unico, però ...)
Va bene in una situazione in cui è necessario eseguire una certa sequenza, indipendentemente dal pezzo più critico posto alla fine.
Per esempio. In motion control comunicazione tra host e controller. In situazioni di emergenza vi sono una serie di passaggi di preparazione per interrompere il movimento, arrestare la generazione della traiettoria, rallentare i motori, abilitare le interruzioni, disabilitare l'amplificatore e successivamente è necessario interrompere l'alimentazione del bus. Tutto deve avvenire in sequenza e se uno dei passaggi fallisce il resto dei passaggi deve essere eseguito comunque anche con il rischio accettato di danneggiare l'hardware. Dì anche se tutto quanto sopra fallisce, la potenza del bus di uccisione deve avvenire comunque.
Chiudere le risorse di sistema con garbo per recuperare da un errore
Per esempio. Se si sta eseguendo un servizio in background che non fornisce il feedback per l'utente, non si può decidere di spingere l'indicazione della errore per l'utente, ma si fare necessità di chiudere le risorse in uso in modo grazioso.
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
Idealmente, dovresti utilizzare la cattura in modalità debug per rilevare errori e stamparli sulla console o su un file di registro per il test. In un ambiente di produzione, i servizi in background generalmente non devono interrompere l'utente quando viene generato un errore, pertanto è necessario eliminare l'output dell'errore.