Perché provare {…} finalmente {…} bene; prova {…} catch {} male?

Ho visto persone dire che è una ctriggers forma usare il 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 mettere il codice cleanup in un blocco finally e inserire il codice cleanup dopo i blocchi try..catch è se si hanno dichiarazioni di ritorno nel proprio blocco try (in tal caso, il codice cleanup infine corri, ma il codice dopo il try..catch non lo farà).

Altrimenti, cosa c’è di così speciale, finalmente?

La grande differenza è che try...catch ingoierà l’eccezione, nascondendo il fatto che si è verificato un errore. try..finally eseguirà il tuo codice di pulizia e quindi l’eccezione continuerà, sarà gestita da qualcosa che sa cosa fare con esso.

“Finalmente” è una dichiarazione di “Qualcosa che devi sempre fare per assicurarti che lo stato del programma sia sano”. Di conseguenza, è sempre buona norma averne uno, se esiste la possibilità che le eccezioni possano scardinare lo stato del programma. Il compilatore fa anche di tutto per assicurarti che il tuo codice Finally venga eseguito.

“Catch” è una dichiarazione di “Posso recuperare da questa eccezione”. Dovresti recuperare solo da eccezioni che puoi veramente correggere – prendere senza argomenti dice “Ehi, posso recuperare da qualsiasi cosa!”, Che è quasi sempre falso.

Se fosse ansible recuperare da ogni eccezione, sarebbe davvero un cavillo semantico, su ciò che stai dichiarando il tuo intento di essere. Tuttavia, non lo è, e quasi certamente i frame sopra i tuoi saranno meglio equipaggiati per gestire alcune eccezioni. Come tale, utilizzare infine, ottenere il codice di pulizia eseguito gratuitamente, ma lasciare che i gestori più esperti affrontino il problema.

Perché quando quella singola linea lancia un’eccezione, non lo sapresti.

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

Con il secondo blocco, l’eccezione verrà lanciata e reader.Close() esplodere ma il reader.Close() È comunque garantito.

Se non è prevista un’eccezione, non inserire un blocco try..catch in questo modo, sarà difficile eseguire il debug in un secondo momento quando il programma è in uno stato negativo e non si ha un’idea del perché.

Finalmente viene eseguito indipendentemente da cosa. Quindi, se il tuo blocco try è riuscito, verrà eseguito, se il tuo blocco try fallisce, eseguirà quindi il blocco catch e quindi il blocco finally.

Inoltre, è meglio provare a utilizzare il seguente costrutto:

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

Poiché l’istruzione using viene automaticamente racchiusa in try / finally e lo stream verrà automaticamente chiuso. (Avrai bisogno di provare / prendere in giro l’istruzione using se vuoi effettivamente catturare l’eccezione).

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

 try { int i = 1/0; } catch { reader.Close(); throw; } try { int i = 1/0; } finally { reader.Close(); } 
  1. ‘finalmente’ è un codice che rivela l’intenzione. Si dichiara al compilatore e ad altri programmatori che questo codice deve essere eseguito indipendentemente da cosa.
  2. se hai più blocchi catch e hai il codice cleanup, devi finalmente. Senza, infine, si duplicherebbe il codice cleanup in ogni blocco catch. (Principio ASCIUTTO)

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

Sono d’accordo con quello che sembra essere il consenso qui – un ‘catch’ vuoto è cattivo perché maschera qualunque eccezione possa essersi verificata nel blocco try.

Inoltre, dal punto di vista della leggibilità, quando vedo un blocco ‘try’ presumo che ci sarà una corrispondente istruzione ‘catch’. Se si sta solo utilizzando un ‘try’ per assicurarsi che le risorse siano deselezionate nel blocco ‘finally’, si potrebbe prendere in considerazione l’ istruzione ‘using’ invece:

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

È ansible utilizzare l’istruzione ‘using’ con qualsiasi object che implementa IDisposable. Il metodo dispose () dell’object viene chiamato automaticamente alla fine del blocco.

Il try..finally block getterà comunque tutte le eccezioni sollevate. Tutto finally fa in modo che il codice di pulizia venga eseguito prima che venga generata l’eccezione.

Il try..atch con una presa vuota consumerà completamente qualsiasi eccezione e nasconderà il fatto che sia successo. Il lettore sarà chiuso, ma non c’è modo di sapere se è accaduta la cosa giusta. E se il tuo intento fosse quello di scrivere io nel file? In questo caso, non potrai accedere a quella parte del codice e myfile.txt sarà vuoto. Tutti i metodi downstream gestiscono questo correttamente? Quando vedrai il file vuoto, sarai in grado di indovinare correttamente che è vuoto perché è stata lanciata un’eccezione? Meglio lanciare l’eccezione e far sapere che stai facendo qualcosa di sbagliato.

Un altro motivo è il 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 pulire dopo? Che mi dici di OutOfMemoryException ? In generale, dovresti gestire solo le eccezioni che ti aspetti e sai come gestire.

Se non sai quale tipo di eccezione prendere o cosa fare con esso, non ha senso avere una dichiarazione di cattura. Dovresti semplicemente lasciarlo per un chiamante di livello superiore che potrebbe avere più informazioni sulla situazione per sapere cosa fare.

Dovresti comunque avere una dichiarazione finale nel caso ci sia un’eccezione, in modo che tu possa ripulire le risorse prima che l’eccezione venga lanciata al chiamante.

Dal punto di vista della leggibilità, è più esplicito dire ai futuri lettori di codice “questa roba qui è importante, deve essere fatta indipendentemente da ciò che accade”. Questo è buono.

Inoltre, le dichiarazioni di cattura vuote tendono ad avere un certo “odore” per loro. Potrebbero essere un segno che gli sviluppatori non stanno pensando attraverso le varie eccezioni che possono verificarsi e come gestirle.

Infine è opzionale – non c’è motivo di avere un blocco “Finalmente” se non ci sono risorse da ripulire.

Tratto da: qui

Alzare e catturare le eccezioni non dovrebbe verificarsi di routine come parte dell’esecuzione corretta di un metodo. Quando si sviluppano le librerie di classi, al codice client deve essere data l’opportunità di verificare una condizione di errore prima di intraprendere un’operazione che può comportare il sorgere 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 un’eccezione potenziale, come illustrato nel seguente frammento di codice:

Dim str As Stream = GetStream () If (str.CanRead) Then ‘code to read stream End If

La decisione di controllare lo stato di un object prima di richiamare un particolare metodo che può generare un’eccezione dipende dallo stato previsto dell’object. Se un object 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’impossibilità di leggere il FileStream sarebbe una violazione del comportamento previsto delle chiamate al metodo effettuate e dovrebbe essere sollevata un’eccezione. Al contrario, se un metodo è documentato come restituire un riferimento FileStream che può essere o meno leggibile, controllare la proprietà CanRead prima di tentare di leggere i dati è consigliabile.

Per illustrare l’impatto sulle prestazioni che l’uso di una tecnica di codifica “esegui fino all’eccezione” può causare, le prestazioni di un cast, che genera una InvalidCastException se il cast fallisce, vengono confrontate con l’operatore C #, che restituisce valori nulli se un cast fallisce. La performance delle due tecniche è identica per il caso in cui il cast è valido (vedi Test 8.05), ma nel caso in cui il cast non è valido, e l’uso di un cast causa un’eccezione, l’uso di un cast è 600 volte più lento rispetto all’utilizzo del cast. come operatore (vedere Test 8.06). L’impatto ad alte prestazioni della tecnica di lancio delle eccezioni include il costo dell’allocazione, del lancio e della cattura dell’eccezione e il costo della successiva garbage collection dell’object di eccezione, il che significa che l’impatto istantaneo del lancio di un’eccezione non è così elevato. Con l’aumento delle eccezioni, la raccolta dei rifiuti frequente diventa un problema, quindi l’impatto generale dell’uso frequente di una tecnica di codifica basata sull’eccezione sarà simile a Test 8.05.

È una ctriggers pratica aggiungere una clausola di catch solo per ribaltare l’eccezione.

Utilizzare Try..Catch..Finally , se il metodo sa come gestire l’eccezione localmente. L’eccezione si verifica in Try, Handled in Catch e, successivamente, la pulizia viene eseguita in Finally.

Nel caso in cui il tuo metodo non sappia come gestire l’eccezione ma ha bisogno di una pulizia una volta che si è verificato usa Try..Finally

Con ciò l’eccezione viene propagata ai metodi di chiamata e gestita se ci sono delle istruzioni di cattura appropriate nei metodi di chiamata. Se non ci sono gestori di eccezioni nel metodo corrente o in nessuno dei metodi di chiamata, l’applicazione si blocca.

Da Try..Finally è garantito che la pulizia locale venga eseguita prima di propagare l’eccezione ai metodi di chiamata.

Alla fine, puoi ripulire le risorse, anche se la tua dichiarazione di cattura lancia l’eccezione fino al programma chiamante. Con il tuo esempio contenente la frase vuota, c’è poca differenza. Tuttavia, se nella cattura, si esegue un po ‘di elaborazione e si genera l’errore, o anche solo non si ha nemmeno un problema, alla fine verrà comunque eseguito.

Bene per uno, è una ctriggers pratica catturare le eccezioni che non si preoccupano di gestire. Scopri il capitolo 5 sulle prestazioni di .Net migliorando le prestazioni e la scalabilità delle applicazioni .NET . Nota a margine, probabilmente dovresti caricare il stream all’interno del blocco try, in questo modo puoi rilevare l’eccezione pertinente se fallisce. La creazione del stream all’esterno del blocco try sconfigge il suo scopo.

Se leggerai C # per i programmatori , capirai che il blocco finale è stato progettato per ottimizzare un’applicazione e prevenire perdite di memoria.

Il CLR non elimina completamente le perdite … si possono verificare perdite di memoria se il programma mantiene inavvertitamente riferimenti a oggetti indesiderati

Ad esempio, quando si apre una connessione di file o database, la macchina assegna la memoria per soddisfare tale transazione e tale memoria verrà mantenuta a meno che non sia stato eseguito il comando di eliminazione o chiusura. ma se durante la transazione si è verificato un errore, il comando in corso verrà terminato non a meno che non si trovasse all’interno del blocco try.. finally..

catch era diverso da finally nel senso che, catch era design per darti modo di gestire / gestire o interpretare l’errore stesso. Pensala come una persona che ti dice “hey ho beccato dei cattivi, cosa vuoi che faccia a loro?” mentre finally stato progettato per assicurarti che le tue risorse fossero posizionate correttamente. Pensa a qualcuno che, indipendentemente dal fatto che ci siano dei cattivi, si assicurerà che la tua proprietà sia ancora al sicuro.

E dovresti permettere a quei due di lavorare insieme per sempre.

per esempio:

 try { StreamReader reader=new StreamReader("myfile.txt"); //do other stuff } catch(Exception ex){ // Create log, or show notification generic.Createlog("Error", ex.message); } finally // Will execute despite any exception { reader.Close(); } 

Tra le molte probabili ragioni, le eccezioni sono molto lente da eseguire. Puoi facilmente azzoppare i tuoi tempi di esecuzione se questo accade molto.

Il problema con i blocchi try / catch che catturano tutte le eccezioni è che il programma è ora in uno stato indeterminato se si verifica un’eccezione sconosciuta. Questo va completamente contro la regola di fail fast: non vuoi che il tuo programma continui se si verifica un’eccezione. Il try / catch sopra potrebbe anche catturare OutOfMemoryExceptions, ma questo è sicuramente uno stato in cui il tuo programma non verrà eseguito.

I blocchi try / finally consentono di eseguire il clean up del codice pur continuando a non riuscire velocemente. Per la maggior parte delle circostanze, vuoi solo catturare tutte le eccezioni a livello globale, in modo che tu possa registrarle e quindi uscire.

La differenza effettiva tra i tuoi esempi è trascurabile finché non vengono lanciate eccezioni.

Se, tuttavia, viene lanciata un’eccezione nella clausola ‘try’, il primo esempio la ingoierà completamente. Il secondo esempio solleverà l’eccezione al successivo step up dello stack di chiamate, quindi la differenza negli esempi indicati è che si oscurano completamente eventuali eccezioni (primo esempio), e l’altro (secondo esempio) conserva le informazioni sulle eccezioni per una successiva manipolazione mentre 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 (sia quella inizialmente sollevata, sia una nuova), il codice di pulitura del lettore non verrebbe mai eseguito. Finalmente viene eseguito indipendentemente da ciò che accade nella clausola ‘catch’.

Quindi, la differenza principale tra “catch” e “finally” è che il contenuto del blocco “finally” (con alcune rare eccezioni) può essere considerato garantito per l’esecuzione, anche a fronte di un’eccezione imprevista, mentre qualsiasi codice che segue una clausola “catch” (ma al di fuori di una clausola “finally”) non avrebbe una tale garanzia.

Incidentalmente, Stream e StreamReader implementano entrambi IDisposable e possono essere racchiusi in un blocco ‘using’. “Usare” i blocchi sono l’equivalente semantico di try / finally (no ‘catch’), quindi il tuo esempio potrebbe essere più tersamente express come:

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

… che chiuderà e eliminerà l’istanza StreamReader quando esce dal campo di applicazione. Spero che questo ti aiuti.

prova {…} catch {} non è sempre male. Non è un modello comune, ma io tendo ad usarlo quando ho bisogno di spegnere le risorse, non importa cosa, come chiudere un socket (possibilmente) aperto alla fine di un thread.