2009-11-12 11 views
10

Capisco che il punto di "utilizzo" è garantire che venga chiamato il metodo Dispose dell'oggetto. Ma come dovrebbe essere gestita un'eccezione all'interno di una frase "using"? Se c'è un'eccezione, ho bisogno di racchiudere la mia frase "using" in un try catch. Per esempio:Perché l'opzione 'using' non ha un blocco catch?


Lets dire che c'è un'eccezione creata nella creazione dell'oggetto all'interno del usando il parametro

try 
{ 
    // Exception in using parameter 
    using (SqlConnection connection = new SqlConnection("LippertTheLeopard")) 
    { 
     connection.Open(); 
    } 
} 
catch (Exception ex) 
{ 

} 

O un'eccezione all'interno del usando ambito

using (SqlConnection connection = new SqlConnection()) 
{ 
    try 
    { 
     connection.Open(); 
    } 
    catch (Exception ex) 
    { 

    } 
} 

Sembra come se Ho già bisogno di gestire un'eccezione con un try catch, che forse dovrei semplicemente gestire lo smaltimento dell'oggetto. In questo caso l'affermazione "using" non sembra affatto aiutarmi. Come gestisco correttamente un'eccezione con l'istruzione "using"? C'è un approccio migliore a questo che mi manca?

SqlConnection connection2 = null; 
try 
{ 
    connection2 = new SqlConnection("z"); 
    connection2.Open(); 
} 
catch (Exception ex) 
{ 

} 
finally 
{ 
    IDisposable disp = connection2 as IDisposable; 
    if (disp != null) 
    { 
     disp.Dispose(); 
    } 
} 

Può la "usando" la sintassi parola chiave un po 'più zuccherino ...
Di certo sarebbe bello avere questo:

using (SqlConnection connection = new SqlConnection()) 
{ 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // What went wrong? Well at least connection is Disposed 
} 

risposta

5

Ho avuto luoghi in cui sarebbe utile. Ma più spesso, quando voglio farlo, si scopre che il problema è nella mia progettazione; Sto cercando di gestire l'eccezione nel posto sbagliato.

Invece, ho bisogno di permetterlo di salire al livello successivo — gestirlo nella funzione che ha chiamato questo codice, piuttosto che proprio lì.

+0

+1 Questo è un grande punto. L'eccezione probabilmente dovrebbe essere gestita al livello successivo. – SwDevMan81

12

using non ha nulla a che fare con la gestione degli errori. È una scorciatoia per "chiama Dispose quando lasci questo blocco". Il tuo secondo esempio di codice è perfettamente accettabile ... perché pasticciare con ciò che funziona?

+0

Il punto della domanda è, perché non utilizzare un blocco catch opzionale. È più retorico a meno che non lavori in Microsoft. – Chap

+0

Non vorrei fare il 2 ° esempio. imo è un esempio di gestione di un errore al livello sbagliato. Lascia che 'bolla in alto' alla funzione chiamante. –

+1

@Joel è d'accordo nella più ampia gamma di cose, questo è il modo in cui lo farei se avessi un motivo per fare una gestione degli errori a quel livello. –

2

Un'idea interessante, ma formula le seguenti po 'di confusione:

using (SqlConnection connection = new SqlConnection()) 
using (SqlCommand cmd = new SqlCommand()) 
{ 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // Is connection valid? Is cmd valid? how would you tell? 
    // if the ctor of either throw do I get here? 
} 
+1

Il tuo esempio non è valido. Le istruzioni "impilate" usando non sono una caratteristica del linguaggio. Il tuo codice equivale ad annidare il secondo usando all'interno del blocco del primo utilizzo. Questo non è diverso rispetto alla scrittura di due istruzioni if ​​back to back senza parentesi graffe. –

+1

Se si desidera catturare singolarmente ciascuna eccezione, è possibile nidificare i blocchi usando. In realtà mi piace l'idea dell'OP anche se sono sicuro che non lo implementeranno mai nella lingua. Non abbastanza utile e potenzialmente confuso. –

+2

@bytenik: è irrilevante. Questo codice potrebbe ancora confondere le persone. Non importa che non sia una caratteristica speciale. –

8

Il blocco usando è solo zucchero sintattico per una prova-blocco finally. Se avete bisogno di una clausola di cattura, basta usare un try-catch-finally:

SqlConnection connection; 
try 
{ 
    connection = new SqlConnection(); 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // handle 
} 
finally 
{ 
    if (connection != null) 
    { 
     connection.Dispose(); 
    } 
} 

Sì, questo è più codice di quella teorica "con catture"; Giudico che gli sviluppatori linguistici non abbiano considerato questa priorità molto alta e non posso dire di aver mai sentito la sua perdita.

+0

Uscirai dal blocco di istruzioni using quando viene lanciata un'eccezione, quindi un try/catch è meglio imo. – ChadNC

+2

@ChadNC: a quanto pare non si capisce "usare" - ti protegge dalle eccezioni e comunque eliminerà l'oggetto. –

27

Perché si "nascondono" funzionalità extra all'interno di una parola chiave non correlata.

Tuttavia si può sempre scrivere in questo modo

using (...) try 
{ 
} 
catch (...) 
{ 
} 

E in questo modo la linea rappresenti le intenzioni - un'istruzione using che è anche una prova

+0

Questo è un modo breve e pulito per farlo. –

+0

Bello, sembra che gestisca qualsiasi errore nell'utilizzo, sebbene non copra l'eccezione nel parametro using. Buono a sapersi però – SwDevMan81

+1

Ooh, mi piace. Vorrei poterlo revocare due volte. –

2

si stia mescolando le preoccupazioni a mio parere. La gestione delle risorse (vale a dire la disposizione degli oggetti) è completamente separata dalla gestione delle eccezioni. Il mapping one-to-one che descrivi nella tua domanda è solo un caso molto speciale. Solitamente la gestione delle eccezioni non si verifica nello stesso punto in cui termina l'ambito di utilizzo. Oppure potresti avere più regioni try-catch all'interno di un blocco using. O ...

+1

lo scopo di un blocco finally è assicurarsi che le risorse vengano pulite. in caso di utilizzo di {try {} catch {} finally {}} l'uso diventa ridondante –

+0

Sì, ma perché usare 'finally'? Nella maggior parte dei casi, il wrapping di 'try/catch' all'interno di' using' è più chiaro _e_ ancora più corto. –

0

Vi consiglio di utilizzare l'esempio n. 1 e n. 2 combinato. Il motivo è che l'istruzione using potrebbe leggere un file, ad esempio, e generare un'eccezione (i.e File Not Found). Se non lo prendi, allora hai un'eccezione non gestita. Mettendo il blocco catch try all'interno del blocco using verranno rilevate solo eccezioni che si verificano dopo l'esecuzione dell'istruzione using. Una combinazione del tuo esempio uno e due è la migliore IMHO.

0

Lo scopo dell'istruzione "using" è quello di garantire che si verifichi un qualche tipo di operazione di pulitura quando l'esecuzione esce da un blocco di codice, indipendentemente dal fatto che tale uscita sia tramite fall-through, un'eccezione o return. Quando il blocco esce da uno di questi mezzi, chiamerà Dispose sul parametro using. Il blocco esiste, in un certo senso, a beneficio di qualsiasi cosa venga specificata come parametro using e, in generale, a questa cosa non interessa il motivo per cui il blocco è stato chiuso.

Ci sono un paio di casi inusuali per i quali le disposizioni potrebbero essere utili; il loro livello di utilità aggiuntiva sarebbe ben al di sotto di quello fornito dal using in primo luogo (anche se probabilmente meglio di altre caratteristiche che gli implementatori ritengono opportuno fornire):

(1) C'è un modello molto comune nei costruttori o fabbriche di oggetti che incapsulano altri oggetti IDisposable; se il costruttore o lo stabilimento escono tramite un'eccezione, gli oggetti incapsulati devono essere Dispose d, ma se escono da return non dovrebbero farlo. Attualmente, tale comportamento deve essere realizzata tramite try/catch, o combinando try/finally con una bandiera, ma sarebbe IMHO essere utile se vi erano o una variazione di using che solo chiamate Dispose quando è uscita tramite eccezione, oppure un keep using dichiarazione che annullerebbe il temporaneo impiegato dall'istruzione using per contenere l'oggetto che necessitava di eliminazione (poiché using non può essere preceduto da un identificatore, tale funzionalità potrebbe essere aggiunta in un modo un po 'analogo a yield return).

(2) In alcuni casi, sarebbe utile se la parola chiave finally fosse estesa per accettare un argomento Exception; trattenere l'eccezione che ha causato la clausola cautelata di uscire (se presente), o null se la clausola cautelata esce normalmente (tramite return o fall-through), e se il blocco using può fare uso di interface IDisposeExOnly {void DisposeEx(Exception ex);} e Interface IDisposeEx : IDisposable, IDisposableExOnly {} (al momento della compilazione) time, selezionato DisposeEx() se implementato, o Dispose() in caso contrario). Ciò potrebbe consentire agli oggetti basati sulle transazioni di supportare in modo sicuro il commit automatico (ovvero eseguire il commit se l'eccezione passata è null o il rollback se non null) e consentirebbe anche il miglioramento della registrazione in situazioni in cui Dispose non riesce come conseguenza di un problema all'interno della clausola protetta (la cosa corretta sarebbe per Dispose lanciare un'eccezione che incapsula sia l'eccezione che era in sospeso quando è stata chiamata, sia l'eccezione che si è verificata di conseguenza, ma al momento non esiste un modo pulito di fare quella).

Non so se Microsoft aggiungerà mai tali funzionalità; la prima e la prima parte del secondo sarebbero gestite interamente a livello di linguaggio. L'ultima parte del secondo sarebbe a livello di struttura.

Problemi correlati