2010-09-03 9 views
6

Uso la dichiarazione using per SqlConnection. È buono per le prestazioni perché forza chiamare Dispose() che rilascia la connessione al pool prima possibile.L'istruzione using può essere sostituita da parentesi graffe?

Tuttavia, mi sono reso conto che l'oggetto creato nell'uso non può essere ridefinito. Non posso fare in questo modo:

using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 
     //... 
     connection = new SqlConnection(connectionString2); 
     //... 
     connection = new SqlConnection(connectionString3); 
    } 

Mi chiedevo se posso sostituire con, e fare qualcosa di simile:

{ 
     SqlConnection connection = new SqlConnection(connectionString); 

     connection.Open(); 
     //... 
     connection = new SqlConnection(connectionString2); 
     //... 
     connection = new SqlConnection(connectionString3); 
} 

Il SqlConnection non sarà accessibile dopo l'ultima parentesi graffa }. Sarà il Dispose() chiamato immediatamente quando l'oggetto esce dal campo di applicazione?

risposta

13

No, le cose non verranno automaticamente ripulite nel tuo secondo esempio (infatti, con il codice che hai, lascerai aperte diverse connessioni aperte).

Non solo, ma si perde la pulizia automatica in caso di eccezioni lanciate all'interno del blocco using. Ricordo che un blocco using si decompone in:

SqlConnection connection = new SqlConnection(connectionString); 
try 
{ 
    connection.Open(); 
    // Do work 
} 
finally 
{ 
    connection.Dispose(); 
} 

Se sei veramente utilizzando connessioni diverse e ogni connessione viene smaltito alla fine del blocco, vorrei utilizzare diversi blocchi utilizzando:

using(SqlConnection connection = new SqlConnection(connectionString)) 
{ 
    connection.Open(); 
    // Do Work 
} 

// First connection is disposed 

using(SqlConnection connection = new SqlConnection(connectionString2)) 
{ 
    // Do More Work 
} 

// Second connection is disposed 

using(SqlConnection connection = new SqlConnection(connectionString3)) 
{ 
    // Do More Work 
} 

// Last connection is dipsosed 
+0

Grazie. Se si tratta di chiudere le connessioni, ovviamente chiamerei connection.Close() ma non l'ho scritto nel codice sopra. – nan

+1

Anche se si chiama esplicitamente Close(), il secondo esempio ha ancora la possibilità di lasciare una connessione aperta se si verifica un'eccezione. Non solo usa la chiamata Dispose quando l'esecuzione lascia quel blocco, ma lo chiama anche quando si verifica un'eccezione. – ThatBlairGuy

3

No, l'utilizzo crea alcuni costrutti di pulizia specifici che non si otterranno solo con le parentesi graffe.

Se si utilizza Reflector e guarda alla IL c'è una chiamata a smaltire aggiunto alla fine del usando di blocco per il suo obiettivo:

L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string) 
L_000b: stloc.0 
L_000c: nop 
L_000d: nop 
L_000e: leave.s L_0020 
L_0010: ldloc.0 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.1 
L_0015: ldloc.1 
L_0016: brtrue.s L_001f 
L_0018: ldloc.0 
**L_0019: callvirt instance void [mscorlib]System.IDisposable::Dispose()** 
L_001e: nop 
L_001f: endfinally 

Questo spiega il motivo per cui non è possibile creare una nuova connessione all'interno del usando il blocco e assegnandolo alla variabile - così facendo lascerebbe il riferimento originale sospeso e non esposto.

+1

Penso che chiami Dispose() su oggetti IDisposable ... non so per certo –

+1

@Mike - È possibile utilizzare solo 'using' su oggetti IDisposable perché chiama Dispose() – annakata

+3

In realtà chiama' ((IDisposable) blah) .Dispose() ', dove' blah' è la variabile che usa. Non si compila se il tipo non implementa 'IDisposable'. – Timwi

3

No, non verrà chiamato dopo la parentesi graffa di chiusura. Devi chiamarlo manualmente o usare una dichiarazione using.

Se non si esegue lo smaltimento, verrà eseguito al termine della finalizzazione. E qui hai 2 problemi

  • L'esecuzione di un metodo di finalizzazione è costosa per le prestazioni. Se il metodo Dispose ha già eseguito il lavoro per ripulire l'oggetto, non è necessario che il garbage collector richiami il metodo Finalize dell'oggetto (se è ben implementato, un metodo Dispose dovrebbe chiamare il metodo SuppressFinalize per l'oggetto che sta eliminando). (MSDN)
  • Non si controlla il momento in cui il finalize viene chiamato automaticamente e non può essere eseguito (perché un arresto anomalo per esempio).
8

Il using affermazione è zucchero sintattico che chiama Dispose sugli oggetti inizializzati all'interno del (), quindi non si può semplicemente sostituirlo come avete nel vostro esempio.

Si noterà che gli unici oggetti che è possibile utilizzare all'interno di una dichiarazione using sono quelli che implementano IDisposable, che assicura che sia possibile chiamare Dispose.

Come this articolo spiega, il compilatore trasformare questa:

using (MyResource myRes = new MyResource()) 
{ 
    myRes.DoSomething(); 

} 

A tal:

MyResource myRes= new MyResource(); 
try 
{ 
    myRes.DoSomething(); 
} 
finally 
{ 
    if (myRes!= null) 
     ((IDisposable)myRes).Dispose(); 
} 

Quindi, a meno che non si duplica questa struttura, non sarà possibile ottenere lo stesso comportamento.

Inoltre, riutilizzare una variabile come nel tuo esempio è una cattiva pratica.Qualcuno che legge il codice potrebbe pensare che stia guardando la connessione 1 quando in realtà sta guardando 2 o 3. Potrebbe finire in modo molto confuso e causare tutti i tipi di problemi lungo la strada.

+0

Beh, ho provato a sbarazzarmi degli utilizzi perché ne ho molti (li uso spesso anche con "Streams" nel mio progetto). La seconda ragione è che ho molte connessioni sequenziali, quindi ho pensato di usare un oggetto. Volevo aumentare la leggibilità, ma hai ragione potrebbe ridurlo. – nan

3
using(foo) 
{ 
    // stuff 
} 

... è un po 'di zucchero che si traduce in:

try 
{ 
    // stuff 
} 
finally 
{ 
    foo.Dispose() 
} 

considerando quanto segue:

{ 
    // stuff 
} 

... non si traduce in nulla. E 'solo:

{ 
    // stuff 
} 

: D

Edit:.. Si prega di non distruggere la formattazione quando si modifica :(

2

No, non può fare a meno in C#

Ma è possibile creare diversi oggetti usa e getta dentro una utilizzando dichiarazione:

using (SqlConnection connection1 = new SqlConnection(connectionString1), 
      connection2 = new SqlConnection(connectionString2), 
      connection3 = new SqlConnection(connectionString3)) 
    { 
     connection1.Open(); 
     //... 
     connection2.Open(); 
     //... 
     connection3.Open(); 
    } 

C++/CLI è possibile utilizzare le vostre classi e getta nello stack simile faision:

void MyClass::Foo() 
{ 

{ 
    SqlConnection connection(connectionString); 
    //connection still allocated on the managed heap 
    connection.Open(); 
    ... 
    //connection will be automatically disposed 
    //when the object gets out of scope 
} 


{ 
    SqlConnection connection(connectionString2); 
    connection2.Open(); 
    ... 
} 

} 
+0

+1 per un bel raggruppamento di 'usings', non lo sapevo – nan

1

ho pensato che anche questo in un primo momento ... ma a quanto pare quando gli utilizzano estremità del blocco, .Dispose() è chiamato sul vostro oggetto, che è diverso da lasciare che l'oggetto esca dallo scope. Dato che C# è un linguaggio spazzato, potrebbe volerci del tempo prima che il tuo oggetto sia effettivamente ripulito. Il blocco using assicura che venga eliminato immediatamente e indipendentemente da eventuali eccezioni.