Domanda

Ho visto persone dire che è una cattiva forma usare catch senza argomenti, specialmente se quel catch non fa nulla:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Tuttavia, questa è considerata una buona forma:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Per quanto ne so, l'unica differenza tra l'inserimento del codice di cleanup in un blocco finally e l'inserimento di un codice di cleanup dopo i blocchi try..catch è se nel blocco try sono presenti dichiarazioni di ritorno (in tal caso, il codice di cleanup infine verrà eseguito, ma il codice dopo il try..catch no).

Altrimenti, cosa c'è di così speciale alla fine?

È stato utile?

Soluzione

La grande differenza è che try ... catch inghiottirà l'eccezione, nascondendo il fatto che si è verificato un errore. try..finally eseguirà il tuo codice di pulizia e quindi l'eccezione continuerà, per essere gestita da qualcosa che sa cosa farne.

Altri suggerimenti

" Infine " è una dichiarazione di " Qualcosa che devi sempre fare per assicurarti che lo stato del programma sia sano " ;. Come tale, è sempre una buona forma averne uno, se esiste la possibilità che le eccezioni possano annullare lo stato del programma. Il compilatore inoltre fa di tutto per assicurarsi che il tuo codice Infine sia eseguito.

" perdere " è una dichiarazione di "posso recuperare da questa eccezione". Dovresti recuperare solo dalle eccezioni che puoi davvero correggere - cattura senza argomenti che dice "Ehi, posso recuperare da qualsiasi cosa!", Che è quasi sempre falso.

Se fosse possibile recuperare da ogni eccezione, sarebbe davvero un cavillo semantico, su ciò che stai dichiarando di essere. Tuttavia, non lo è, e quasi sicuramente i frame sopra i tuoi saranno meglio equipaggiati per gestire alcune eccezioni. Pertanto, utilizza infine, esegui il codice di pulizia gratuitamente, ma lascia che gestori più competenti affrontino il problema.

Perché quando quella singola riga genera un'eccezione, non lo sapresti.

Con il primo blocco di codice, l'eccezione sarà semplicemente assorbita , il programma continuerà a essere eseguito anche quando lo stato del programma potrebbe essere errato.

Con il secondo blocco, l'eccezione verrà generata e bolle ma il reader.Close () è comunque garantito per essere eseguito.

Se non si prevede un'eccezione, non mettere un blocco try..catch proprio così, sarà difficile eseguire il debug in seguito quando il programma è andato in cattivo stato e non hai idea del perché.

Finalmente viene eseguito qualunque cosa. Pertanto, se il blocco try ha esito positivo, verrà eseguito, se il blocco try non riesce, eseguirà il blocco catch e quindi il blocco finally.

Inoltre, è meglio provare a usare il seguente costrutto:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Poiché l'istruzione using viene automaticamente racchiusa in un tentativo / infine e lo stream verrà automaticamente chiuso. (Dovrai mettere un try / catch intorno all'istruzione using se vuoi davvero catturare l'eccezione).

Sebbene i seguenti 2 blocchi di codice siano equivalenti, non sono uguali.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 'finally' è un codice che rivela l'intenzione. Dichiarate al compilatore e ad altri programmatori che questo codice deve essere eseguito indipendentemente da cosa.
  2. se hai più blocchi catch e hai un codice di pulizia, devi finalmente. Senza infine, duplicheresti il ??codice di pulizia in ogni blocco catch. (Principio DRY)

infine i blocchi sono speciali. Il CLR riconosce e tratta il codice con un blocco di fine separatamente separatamente dai blocchi di cattura e il CLR fa di tutto per garantire che un blocco di fine venga sempre eseguito. Non è solo lo zucchero sintattico del compilatore.

Sono d'accordo con quello che sembra essere il consenso qui - un "catch" vuoto è negativo perché maschera qualsiasi eccezione si sia verificata nel blocco try.

Inoltre, dal punto di vista della leggibilità, quando vedo un blocco 'try' presumo che ci sarà un'istruzione 'catch' corrispondente. Se si utilizza solo un "tentativo" per assicurarsi che le risorse siano allocate nel blocco "finally", è possibile considerare istruzione 'using' invece:

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Puoi usare l'istruzione 'using' con qualsiasi oggetto che implementa IDisposable. Il metodo dispose () dell'oggetto viene chiamato automaticamente alla fine del blocco.

Il blocco try..finally genererà comunque qualsiasi eccezione sollevata. Tutto il infine è garantire che il codice di pulizia venga eseguito prima che venga generata l'eccezione.

Il try..catch con un catch vuoto consumerà completamente qualsiasi eccezione e nasconderà il fatto che è successo. Il lettore sarà chiuso, ma non si può dire se sia avvenuta la cosa giusta. E se il tuo intento fosse scrivere i nel file? In questo caso, non arriverai a quella parte del codice e myfile.txt sarà vuoto. Tutti i metodi a valle lo gestiscono correttamente? Quando vedi il file vuoto, sarai in grado di indovinare correttamente che è vuoto perché è stata generata un'eccezione? Meglio gettare l'eccezione e far sapere che stai facendo qualcosa di sbagliato.

Un altro motivo è che try..catch fatto in questo modo è completamente errato. Quello che stai dicendo facendo questo è, "Non importa cosa succede, posso gestirlo." Che dire di StackOverflowException , puoi ripulire dopo? Che dire di OutOfMemoryException ? In generale, dovresti gestire solo le eccezioni che ti aspetti e sapere come gestire.

Usa Try..Catch..Finally , se il tuo metodo sa come gestire l'eccezione localmente. L'eccezione si verifica in Try, Handled in Catch e dopo che la pulizia è stata eseguita in Final.

Nel caso in cui il metodo non sappia come gestire l'eccezione ma necessita di una pulizia una volta verificatasi, utilizzare Try..Finally

In questo modo l'eccezione viene propagata ai metodi chiamanti e gestita se ci sono istruzioni Catch adatte nei metodi chiamanti. Se non ci sono gestori eccezioni nel metodo corrente o in uno dei metodi chiamanti, l'applicazione si arresta in modo anomalo.

Con Try..Finally è assicurato che la pulizia locale venga eseguita prima di propagare l'eccezione ai metodi di chiamata.

Se non sai quale tipo di eccezione catturare o cosa farne, non ha senso avere una dichiarazione catch. Dovresti semplicemente lasciarlo per un chiamante più in alto che potrebbe avere maggiori informazioni sulla situazione per sapere cosa fare.

Dovresti comunque avere un'istruzione finally in caso ci sia un'eccezione, in modo da poter ripulire le risorse prima che quell'eccezione venga lanciata al chiamante.

Dal punto di vista della leggibilità, è più esplicito raccontare ai futuri lettori di codici "questa roba qui è importante, deve essere fatta qualunque cosa accada." Questo è buono.

Inoltre, le dichiarazioni di cattura vuote tendono ad avere un certo "odore" a loro. Potrebbero essere un segno che gli sviluppatori non stanno riflettendo sulle varie eccezioni che possono verificarsi e su come gestirle.

Finalmente è facoltativo - non c'è motivo di avere un " Infine " bloccare se non ci sono risorse da ripulire.

Tratto da: qui

Generare e catturare eccezioni non dovrebbe avvenire di routine come parte dell'esecuzione corretta di un metodo. Quando si sviluppano librerie di classi, è necessario offrire al codice client l'opportunità di verificare una condizione di errore prima di intraprendere un'operazione che può comportare la generazione di un'eccezione. Ad esempio, System.IO.FileStream fornisce una proprietà CanRead che può essere verificata prima di chiamare il metodo Read, evitando che venga sollevata una potenziale eccezione, come illustrato nel seguente frammento di codice:

Dim str As Stream = GetStream () If (str.CanRead) Quindi   'codice per leggere lo stream End If

La decisione di verificare lo stato di un oggetto prima di invocare un metodo particolare che può sollevare un'eccezione dipende dallo stato previsto dell'oggetto. Se un oggetto FileStream viene creato utilizzando un percorso file che dovrebbe esistere e un costruttore che dovrebbe restituire un file in modalità lettura, non è necessario controllare la proprietà CanRead; l'incapacità di leggere il FileStream sarebbe una violazione del comportamento previsto delle chiamate al metodo effettuate e dovrebbe essere sollevata un'eccezione. Al contrario, se è documentato un metodo che restituisce un riferimento FileStream che può essere o meno leggibile, è consigliabile controllare la proprietà CanRead prima di tentare di leggere i dati.

Per illustrare l'impatto sulle prestazioni che l'utilizzo di una "corsa fino all'eccezione" la tecnica di codifica può causare, l'esecuzione di un cast, che genera un InvalidCastException se il cast ha esito negativo, viene confrontata con C # come operatore, che restituisce valori null in caso di fallimento di un cast. Le prestazioni delle due tecniche sono identiche per il caso in cui il cast è valido (vedere Test 8.05), ma per il caso in cui il cast non è valido e l'utilizzo di un cast provoca un'eccezione, l'utilizzo di un cast è 600 volte più lento rispetto all'uso del come operatore (vedi Test 8.06). L'impatto ad alte prestazioni della tecnica di lancio delle eccezioni include il costo di allocazione, lancio e cattura dell'eccezione e il costo della successiva raccolta dei rifiuti dell'oggetto eccezione, il che significa che l'impatto istantaneo del lancio di un'eccezione non è così elevato. Man mano che vengono generate più eccezioni, la raccolta dei rifiuti frequente diventa un problema, quindi l'impatto complessivo dell'uso frequente di una tecnica di codifica che genera eccezioni sarà simile al Test 8.05.

È una cattiva pratica aggiungere una clausola catch solo per riproporre l'eccezione.

Con finalmente, puoi ripulire le risorse, anche se la tua dichiarazione catch lancia l'eccezione al programma chiamante. Con il tuo esempio contenente l'istruzione catch vuota, c'è poca differenza. Tuttavia, se nella tua cattura, esegui un po 'di elaborazione e lanci l'errore, o anche semplicemente non hai nemmeno una presa, finalmente verrà comunque eseguito.

Beh, per uno, è una cattiva pratica catturare eccezioni che non ti preoccupi di gestire. Consulta Capitolo 5 sulle prestazioni .Net da Miglioramento. Prestazioni e scalabilità delle applicazioni NET . Nota a margine, probabilmente dovresti caricare il flusso all'interno del blocco try, in questo modo, puoi catturare l'eccezione pertinente se fallisce. La creazione dello stream all'esterno del blocco try ne vanifica lo scopo.

Tra probabilmente molte ragioni, le eccezioni sono molto lente da eseguire. Puoi facilmente paralizzare i tempi di esecuzione se ciò accade molto.

Il problema con i blocchi try / catch che rilevano tutte le eccezioni è che il programma è ora in uno stato indeterminato se si verifica un'eccezione sconosciuta. Questo va completamente contro la regola del fail fast - non vuoi che il tuo programma continui se si verifica un'eccezione. Il precedente tentativo / cattura catturerebbe anche OutOfMemoryExceptions, ma questo è sicuramente uno stato in cui il tuo programma non verrà eseguito.

I blocchi try / finally ti consentono di eseguire il codice di pulizia pur fallendo velocemente. Nella maggior parte dei casi, si desidera solo catturare tutte le eccezioni a livello globale, in modo da poterle registrare e quindi uscire.

La differenza effettiva tra i tuoi esempi è trascurabile purché non vengano generate eccezioni.

Se, tuttavia, viene generata un'eccezione nella clausola 'try', il primo esempio la inghiottirà completamente. Il secondo esempio solleverà l'eccezione al passaggio successivo dello stack di chiamate, quindi la differenza negli esempi dichiarati è che uno oscura completamente qualsiasi eccezione (primo esempio) e l'altro (secondo esempio) conserva le informazioni sull'eccezione per una potenziale gestione successiva mentre sta ancora eseguendo il contenuto nella clausola "finally".

Se, ad esempio, dovessi inserire il codice nella clausola 'catch' del primo esempio che ha generato un'eccezione (o quella inizialmente sollevata o nuova), il codice di pulizia del lettore non verrebbe mai eseguito. Infine esegue indipendentemente da ciò che accade nella clausola "catch".

Quindi, la differenza principale tra 'catch' e 'finally' è che i contenuti del blocco 'finally' (con alcune rare eccezioni) possono essere considerati garantiti da eseguire, anche nel di fronte a un'eccezione imprevista, mentre qualsiasi codice che segue una clausola "catch" (ma al di fuori di una clausola "finally") non avrebbe tale garanzia.

Per inciso, Stream e StreamReader implementano entrambi IDisposable e possono essere racchiusi in un blocco 'using'. I blocchi "using" sono l'equivalente semantico di try / finally (no "catch"), quindi il tuo esempio potrebbe essere espresso più tersamente come:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... che chiuderà e eliminerà l'istanza StreamReader quando non rientra nell'ambito. Spero che questo aiuti.

provare {…} catturare {} non è sempre male. Non è un modello comune, ma tendo a usarlo quando ho bisogno di arrestare le risorse, qualunque cosa accada, come chiudere un socket (forse) aperto alla fine di un thread.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top