2014-10-20 17 views
13


Il MSDN recommends mette un'istanza delle classi che implementano IDisposable in un blocco using. In alternativa, se viene istanziato all'interno di un blocco try-catch, quindi Dispose in Finally.Ci sono problemi con l'utilizzo del blocco per un IDisposable all'interno di un blocco catch catch?

Ci sono problemi nell'utilizzo di un blocco using all'interno di un blocco try-catch in questo modo?

try 
{ 
    using (Foo bar = new Foo()) 
    { 
     bar.doStuff(); 
    } 
} 
catch (Exception e) 
{ 
    //vomit e 
} 

Naturalmente posso solo chiamare Dispose in un blocco Finally, ma io sono un novizio di programmazione e io sono solo curioso di sapere se fare qualcosa di simile è praticamente accettabile o se qualcuno mi schiaffo up il retro della mia testa e urla contro di me che sono Doing-It-Wrong™.

O meglio, sono più interessato a sapere perché questo sarebbe sbagliato se lo fosse.

+1

No, questo va bene. Perché dovrebbe essere sbagliato? Anche se in questo caso userò il try-catch-finally, visto che c'è già un blocco 'try' (nel caso generale questo potrebbe davvero importare se è necessario usare' bar' nel 'catch', ma non si nel tuo esempio). – Cameron

+2

L'istruzione using eliminerà l'oggetto e il catch try gestirà eventuali errori. Come stai facendo sopra è assolutamente soddisfacente –

+2

Questa è una buona idea perché promuove la coerenza per quanto riguarda lo smaltimento. Usa sempre se puoi. Regola semplice, quasi sempre una buona linea guida. – usr

risposta

9

No, sembra perfetto. L'istanza della barra verrà eliminata prima ancora di entrare nel blocco catch.

+2

Aha, l'istanza della barra verrà eliminata prima che raggiunga anche il blocco catch. Questo è quello che non avevo capito e perché l'ho chiesto. E come altri hanno anche sottolineato che rende problematico se si volesse utilizzare la barra per qualcosa durante la cattura. Grazie a tutti per le risposte :) –

1

Il codice di esempio è ridondante. Il Using() documentation states:

A utilizzando enunciato della forma

using (ResourceType resource = expression) 

dichiarazione corrisponde ad uno dei due possibili espansioni. Quando ResourceType è un tipo di valore, l'espansione è

{ 
    ResourceType resource = expression; 
    try { 
     statement; 
    } 
    finally { 
     ((IDisposable)resource).Dispose(); 
    } 
} 

Altrimenti, quando ResourceType è un tipo di riferimento, l'espansione è

{ 
    ResourceType resource = expression; 
    try { 
     statement; 
    } 
    finally { 
     if (resource != null) ((IDisposable)resource).Dispose(); 
    } 
} 

In entrambi espansione, la variabile risorsa è di sola lettura nel incorporati dichiarazione.

Il codice in ultima analisi, un aspetto simile:

try 
{ 
    Foo bar = new Foo() 
    try 
    { 
     bar.doStuff(); 
    } 
    finally 
    { 
     if (bar != null) ((IDisposable)bar).Dispose(); 
    } 
} 
catch (Exception e) 
{ 
    //vomit e 
} 

alcun motivo reale per due istruzioni try. Non è codice errato, è ridondante nel contesto di più istruzioni di prova. La tua domanda sembra essere circa Disposing di un oggetto. In questo contesto, è ridondante. Se sei anche preoccupato per il costruttore di oggetti che lancia un'eccezione, ovviamente questo sarebbe necessario.

Ci sono problemi con l'utilizzo di un blocco utilizzando all'interno di un blocco try-catch in questo modo?

No, scrivo il tuo esempio tutto il tempo.

Naturalmente posso solo chiamare Dispose in un Infine blocco,

Una specie di, il costruttore deve essere chiamato fuori del try/catch, altrimenti la variabile sarà fuori del campo di applicazione con il tempo si raggiungere il blocco finally

valida:

var foo = new bar(); 
try 
{ 
} 
finally 
{ 
    foo.Dispose(); 
} 

non valida:

try 
{ 
    var foo = new bar(); 
} 
finally 
{ 
    foo.Dispose(); 
} 
+0

Cos'è ridondante? Gli snippet di codice non catturano nulla. – usr

+1

Quale parte del codice è ridondante? – nvoigt

+0

Perché questo rende il codice dell'OP ridondante? Dove * esattamente * vedi la ridondanza? (È possibile che la domanda sia cambiata da quando hai postato la tua risposta, ovviamente, ma se così fosse, dovresti modificare anche la tua risposta.) –

6

Questo è ... rullo di tamburi ... assolutamente soddisfacente.

L'unico problema è che non si dovrebbe usare catch (Exception), ma prendere gli errori specifici che ti interessano (anche se questo è solo un esempio), ma questo non ha alcun rapporto con lo using. In effetti, l'unico motivo per cui ilviene inserito al di fuori del blocco try è perché si desidera continuare a fare qualcosa con bar che non è correlato al codice che non è riuscito, in caso contrario mantenere l'ambito il più piccolo possibile è generalmente una buona idea.

+0

@Aron a meno che non si desideri specificamente la traccia dello stack dal gestore degli errori - per indicare che la gestione degli errori non è riuscita, ovvero. –

+1

@Aron: Rethrowing rovina un po 'troppo la traccia dello stack, IIRC. (Il danno è limitato al fotogramma corrente, ma invece della linea che ha chiamato il codice che ha causato l'errore, tu invece vedi il punto di rethrow.) Se non puoi recuperare, e non devi ripulire, don ' t prendere. – cHao

1

No, va bene, ma se si desidera accedere bar durante la catch, avrete bisogno di un interno try catch:

try 
{ 
    using (Foo bar = new Foo()) 
    { 
     try 
     { 
     bar.doStuff(); 
     } 
     catch (Exception e) 
     { 
     //vomit e, with bar available. 
     } 
    } 
} 
catch (Exception e) 
{ 
    //vomit e, relating to a problem during creation of Foo. 
} 

o, come suggerito nei commenti, fattore il blocco interno in un nuovo metodo

+2

Se * stai * andando su quella rotta, probabilmente vuoi calcolare il blocco interno con un nuovo metodo, perché i blocchi nidificati di 'try' \' catch' sono piuttosto difficili da leggere (anche più di altre cose annidate, intendo). –

4

È OK, ma hai perso l'accesso all'oggetto che avrebbe causato l'eccezione nell'eccezione.
e la cattura di eccezioni generali non è considerata una buona pratica

Foo bar = null; 
try 
{ 
    bar = new Foo(); 
    bar.doStuff(); 
} 
catch (IndexOutOfRangeException e) 
{ 
    //vomit e 
    Debug.WriteLine(e.msg); 
    if(bar == null) 
     Debug.WriteLine("bar = new Foo() failed "); 
    else 
     Debug.WriteLine("bar fail ID = " + bar.ID); 
} 
catch (Exception e) 
{ 
    // ... 
    // unless you are going to handle it gracefully you should rethrow it 
} 
finally 
{ 
    if(bar != null) bar.Dispose(); 
} 
+1

@JeroenMostert Aggiunto null e bar.doStuff potrebbe non riuscire. – Paparazzi

+0

Funziona, ma i controlli multipli 'nulli' sono scomodi - e trovo difficile immaginare uno scenario reale in cui si voglia gestire un errore sia costruendo un' Foo' che chiamando un metodo su di esso nello stesso blocco. Il blocco esterno è quasi * ma non del tutto * ciò che fa "using", quindi sarei tentato di riscriverlo. –

+0

@JeroenMostert Se preferisci avere due blocchi di cattura di due assegni per null, allora vai a farlo. Non riesco a immaginare un singolo scenario in cui preferirei avere due prese. – Paparazzi

Problemi correlati