2010-08-02 9 views
17

La maggior parte degli esempi di istruzione using in C# dichiarano l'oggetto all'interno delle parentesi in questo modo:Può un oggetto essere dichiarato sopra un'istruzione using anziché nelle parentesi

using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection)) 
{ 
    // Code goes here 
} 

Cosa succede se uso il tramite dichiarazione nel modo seguente con l'oggetto dichiarato fuori l'istruzione using:

SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection); 
using (cmd) 
{ 
    // Code goes here 
} 

e 'una cattiva idea di utilizzare l'istruzione using nel modo che ho in secondo esempio e perché?

risposta

15

Dichiarazione della variabile all'interno espressione controllo utilizzando della dichiarazione limita l'ambito della variabile all'interno della istruzione using. Nel secondo esempio è possibile continuare a utilizzare la variabile cmd dopo l'istruzione using (quando sarà stata eliminata).

Generalmente si raccomanda di utilizzare solo una variabile per uno scopo, limitando la sua portata permette un altro comando con lo stesso nome in seguito portata (magari in un altro utilizzando espressione). Forse ancora più importante racconta un lettore del codice (e la manutenzione richiede uno sforzo maggiore rispetto scrittura iniziale) che cmd non viene utilizzato al di là l'istruzione using: il codice è un po 'più comprensibile.

6

Sì, valido - l'oggetto sarà ancora disposto nello stesso modo, cioè, alla fine e se il flusso di esecuzione tenta di lasciare il blocco (ritorno/eccezione).

Tuttavia, se si tenta di riutilizzarlo dopo lo using, sarà stato eliminato, quindi non è possibile sapere se tale istanza è sicura per continuare ad utilizzare poiché dispose non deve reimpostare lo stato dell'oggetto. Inoltre, se si verifica un'eccezione durante la costruzione, non avrà colpito il blocco using.

Vorrei dichiarare e inizializzare la variabile all'interno dell'istruzione per definirne l'ambito. Le probabilità sono molto buone che non sarà necessario che al di fuori del campo di applicazione, se si utilizza un using comunque.

MemoryStream ms = new MemoryStream(); // Initialisation not compiled into the using. 

using (ms) { } 

int i = ms.ReadByte(); // Will fail on closed stream. 

seguente è valida, ma un po 'inutile nella maggior parte dei casi:

MemoryStream ms = null; 

using (ms = new MemoryStream()) 
{ } 

// Do not continue to use ms unless re-initializing. 
+0

E sarà ancora disponibile dopo il 'using'. – Anax

+2

Sarà disponibile ma non utilizzabile - lo stato interno sarà stato eliminato. Non hai idea di cosa sia lo stato interno o di come dispose l'istanza della classe, quindi non è un corso sicuro. Non ho detto che non sarebbe disponibile, il -1 non è applicabile. –

+0

Credo che anche se il costruttore viene eseguito all'interno del blocco using, Dispose non verrà chiamato se il costruttore getta. – Henrik

1

L'idea alla base è quella di usingdefine a scope, outside of which an object or objects will be disposed.

Se si dichiara l'oggetto che si sta per utilizzare all'interno di using in anticipo, non ha senso utilizzare l'istruzione using.

+1

Tecnicamente c'è ancora un punto, poiché si comporta ancora correttamente e dispone dell'oggetto. * L'inizializzazione * al di fuori del blocco di utilizzo potrebbe essere comunque pericolosa, in quanto ciò non sarà coperto dal comportamento del blocco di utilizzo. –

1

E 'stata una risposta e la risposta è: Sì, è possibile.
Tuttavia, dal punto di vista dei programmatori, non farlo! Confonderà qualsiasi programmatore che lavorerà su questo codice e che non si aspetta una tale costruzione. Fondamentalmente, se date il codice a qualcun altro su cui lavorare, quell'altra persona potrebbe finire per essere molto confusa se usa la variabile "cmd" dopo l'utilizzo dello. Ciò diventa ancora peggio se ci sono ancora più righe di codice tra la creazione dell'oggetto e la parte "using".

1

Ho scritto un piccolo codice insieme ad alcuni test unitari. Mi piace quando posso convalidare le affermazioni sulla domanda in questione.I miei risultati:

  • Se un oggetto è stato creato prima o nell'istruzione using non ha importanza. Deve implementare IDisposable e Dispose() verrà richiamato all'uscita dal blocco di istruzioni using (parentesi di chiusura).
  • Se il costruttore genera un'eccezione quando invocato nell'istruzione using Dispose() non verrà richiamata. Questo è ragionevole in quanto l'oggetto non è stato costruito correttamente quando viene generata un'eccezione nel costruttore. Pertanto non esiste alcuna istanza in quel punto e chiamare i membri dell'istanza (membri non statici) sull'oggetto non ha senso. Questo include Dispose().

Per riprodurre i risultati, consultare il codice sorgente riportato di seguito.

In questo modo è possibile, come indicato da altri, istanziare un oggetto prima dell'istruzione using e quindi utilizzarlo all'interno dell'istruzione using. Sono anche d'accordo, tuttavia, spostare la costruzione al di fuori dell'istruzione using porta a un codice che è meno leggibile.

Un altro elemento di cui si può essere a conoscenza è il fatto che alcune classi possono generare un'eccezione nell'implementazione Dispose(). Sebbene la linea guida non lo faccia, anche Microsoft ha casi di questo, ad es. come discusso here.

ecco il mio codice sorgente includono un test (lungo):

public class Bar : IDisposable { 
    public Bar() { 
     DisposeCalled = false; 

    } 
    public void Blah() { 
     if (DisposeCalled) { 
      // object was disposed you shouldn't use it anymore 
      throw new ObjectDisposedException("Object was already disposed."); 
     } 
    } 

    public void Dispose() { 
     // give back/free up resources that were used by the Bar object 
     DisposeCalled = true; 
    } 

    public bool DisposeCalled { get; private set; } 
    } 

    public class ConstructorThrows : IDisposable { 
    public ConstructorThrows(int argument) { 
     throw new ArgumentException("argument"); 
    } 

    public void Dispose() { 
     Log.Info("Constructor.Dispose() called."); 
    } 
    } 

    [Test] 
    public void Foo() { 
    var bar = new Bar(); 
    using (bar) { 
     bar.Blah(); // ok to call 
    }// Upon hitting this closing brace Dispose() will be invoked on bar. 

    try { 
     bar.Blah(); // Throws ObjectDisposedException 
     Assert.Fail(); 
    } 
    catch(ObjectDisposedException) { 
     // This exception is expected here 
    } 

    using (bar = new Bar()) { // can reuse the variable, though 
     bar.Blah(); // Fine to call as this is a second instance. 
    } 

    // The following code demonstrates that Dispose() won't be called if 
    // the constructor throws an exception: 
    using (var throws = new ConstructorThrows(35)) { 

    } 
    } 
+0

Se il costruttore lancia, il riferimento non viene mai assegnato alla variabile - non vi è alcun riferimento all'oggetto attraverso il quale chiamare 'Dispose'. – Richard

Problemi correlati