try / catch + using, sintassi corretta


189

Quale:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

O

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}

7
Solo una nota: si dovrebbe fare attenzione a catturare solo le eccezioni che possono essere effettivamente gestite (corrette), ad eccezione della registrazione o del wrapping.
John Saunders,

1
Si prega di tenere a mente che anche l'ultimo }della usingdichiarazione può generare un'eccezione come ricordato qui .
Giulio Caccin,

1
TIL che il debugger (in VS) non chiamerà il metodo dispose se si utilizza il primo blocco di codice. Poiché l'istruzione using stessa può generare un'eccezione, mi aiuta a utilizzare il secondo blocco per garantire che il finallymetodo dispose sia chiamato implicito .
ShooShoSha,

Risposte:


98

Preferisco il secondo. Può anche intrappolare errori relativi alla creazione dell'oggetto.


11
Non sono d'accordo con questo consiglio. Se si prevede che la creazione dell'oggetto genererà un errore, qualsiasi gestione di tale eccezione deve andare all'esterno. Se c'è qualche domanda su dove dovrebbe andare la gestione, allora l'eccezione che ci si aspetta deve essere qualcos'altro — a meno che tu non stia sostenendo la cattura di qualsiasi eccezione casuale che può o non può essere anticipata, che è un classico anti-pattern (al di fuori di un Gestore eccezioni non gestite del processo o del thread).
Jeffrey L Whitledge,

1
@Jeffrey: l'approccio che ho descritto mi è servito bene e lo sto facendo da molto tempo. Nessuno ha detto nulla sull'aspettarsi che la creazione di oggetti fallisse. Ma avvolgendo un'operazione che potrebbe potenzialmente fallire in un tryblocco, che consente di visualizzare un messaggio di errore in caso di errore, il programma ora ha la possibilità di recuperare e informare l'utente.
Jonathan Wood,

La tua risposta è corretta ma continua il suggerimento che il try / catch deve essere presente (immediatamente) in ogni momento.
Henk Holterman,

17
Penso che anche il primo meriti, considera una transazione DB using( DBConnection conn = DBFactory.getConnection())che dovrebbe essere ripristinata in caso di un'eccezione. Mi sembra che entrambi abbiano il loro posto.
wfoster,

1
Ciò intrappolerà anche errori relativi allo smaltimento dell'oggetto.
Ahmad Ibrahim,

39

Poiché un blocco using è solo una semplificazione della sintassi di un try / finally ( MSDN ), personalmente sceglierei quanto segue, anche se dubito che sia significativamente diverso dalla tua seconda opzione:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}

4
Perché pensi che l'aggiunta di un finallyblocco sia preferibile usingall'istruzione?
Cody Grey

10
L'aggiunta di un finallyblocco che elimina un oggetto IDisposable è ciò usingche fa un'istruzione. Personalmente, mi piace questo invece del usingblocco incorporato perché penso che affermi più chiaramente dove sta succedendo tutto e che è tutto sullo stesso "livello". Mi piace anche questo più di diversi usingblocchi incorporati ... ma è solo una mia preferenza.
chezy525,

8
Se implementi molta gestione delle eccezioni, devi davvero divertirti a digitare! Quella parola chiave "utilizzo" esiste da un po 'di tempo e il suo significato è abbastanza chiaro per me. E usarlo aiuta a rendere più chiaro il resto del mio codice mantenendo al minimo la quantità di disordine.
Jonathan Wood,

2
Questo non è corretto L'oggetto deve essere istanziato all'esterno dell'istruzione tryper essere disposto all'interno dell'istruzione finally; altrimenti genererà un errore del compilatore: "Uso della variabile locale non assegnata 'myObject'"
Steve Konves,

3
Tecnicamente, neanche quello verrà compilato. Cannot assign null to implicitly-typed local variable;) Ma so cosa intendi e personalmente preferirei questo a nidificare un blocco usando.
Connell

20

Dipende. Se si utilizza Windows Communication Foundation (WCF), using(...) { try... }non funzionerà correttamente se il proxy usingnell'istruzione si trova in stato di eccezione, ovvero l'eliminazione di questo proxy causerà un'altra eccezione.

Personalmente, credo nell'approccio di gestione minima, ovvero gestire solo le eccezioni di cui sei a conoscenza al momento dell'esecuzione. In altre parole, se sai che l'inizializzazione di una variabile usingpuò generare una particolare eccezione, la avvolgo con try-catch. Allo stesso modo, se all'interno del usingcorpo può accadere qualcosa, che non è direttamente correlato alla variabile in using, allora la avvolgo con un'altra tryper quella particolare eccezione. Uso raramente Exceptionnei miei catches.

Ma mi piace IDisposablee usinganche se forse sono di parte.


19

Se la tua dichiarazione catch deve accedere alla variabile dichiarata in un'istruzione using, allora all'interno è l'unica opzione.

Se la tua dichiarazione catch ha bisogno dell'oggetto a cui fa riferimento l'utilizzo prima di essere eliminata, allora all'interno è l'unica opzione.

Se la tua dichiarazione di cattura intraprende un'azione di durata sconosciuta, come la visualizzazione di un messaggio all'utente e desideri disporre delle tue risorse prima che ciò accada, allora l'esterno è l'opzione migliore.

Ogni volta che ho uno scenario simile a questo, il blocco try-catch è di solito in un metodo diverso più avanti nello stack di chiamate dall'uso. Non è tipico per un metodo sapere come gestire le eccezioni che si verificano al suo interno in questo modo.

Quindi la mia raccomandazione generale è fuori, molto fuori.

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

10

Entrambe sono sintassi valide. Dipende davvero da cosa vuoi fare: se vuoi catturare errori relativi alla creazione / eliminazione dell'oggetto, usa il secondo. In caso contrario, utilizzare il primo.


8

C'è una cosa importante che chiamerò qui: la prima non catturerà alcuna eccezione derivante dalla chiamata al MyClasscostruttore.


3

Da C # 8.0, preferisco usare il secondo come questo

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

e poi

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }

1

Se l'oggetto che si sta inizializzando nel blocco Using () potrebbe generare un'eccezione, è necessario scegliere la seconda sintassi, altrimenti entrambe ugualmente valide.

Nel mio scenario, ho dovuto aprire un file e stavo passando filePath nel costruttore dell'oggetto che stavo inizializzando nel blocco Using () e potrebbe generare un'eccezione se filePath è errato / vuoto. Quindi, in questo caso, la seconda sintassi ha senso.

Il mio codice di esempio: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}

1

Da C # 8.0 in poi , è possibile semplificare le usingistruzioni in alcune condizioni per eliminare il blocco nidificato, quindi si applica solo al blocco che lo racchiude.

Quindi i tuoi due esempi possono essere ridotti a:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

E:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Entrambi sono abbastanza chiari; e quindi ciò riduce la scelta tra i due a una questione di ciò che si desidera raggiungere l'ambito dell'oggetto, dove si desidera gestire gli errori di istanza e quando si desidera eliminarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.