2010-09-03 12 views
6

Używam instrukcji using dla SqlConnection. Jest to dobre dla wydajności, ponieważ siły wywołujące Dispose() po prostu zwalniają połączenie z pulą wcześniej.Czy instrukcję using można zastąpić nawiasami klamrowymi?

Jednak zdałem sobie sprawę, że obiekt utworzony przy użyciu nie może być ponownie zdefiniowany. Nie mogę tego zrobić tak:

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

Zastanawiałem się, czy mogę zastąpić używania i zrobić coś takiego:

{ 
     SqlConnection connection = new SqlConnection(connectionString); 

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

SqlConnection nie będzie dostepne po ostatnim } klamrą. Czy funkcja Dispose() zostanie wywołana natychmiast po wykryciu obiektu poza zakresem?

Odpowiedz

13

Nie, rzeczy nie zostaną automatycznie wyczyszczone w drugim przykładzie (w rzeczywistości z kodem, który masz, pozostawisz kilka połączeń zawieszonych).

Co więcej, ale w przypadku wyrzucenia wyjątków w bloku using tracisz automatyczne czyszczenie. Pamiętaj, że using blok rozkłada się na:

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

Jeśli naprawdę przy użyciu różnych połączeń, a każde połączenie jest usuwany na końcu bloku, chciałbym używać kilku wykorzystaniem bloków:

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

Dzięki. Jeśli chodzi o zamykanie połączeń, oczywiście zadzwoniłbym connection.Close(), ale nie zapisałem go w powyższym kodzie. – nan

+1

Nawet jeśli wywołasz Close() jawnie, twój drugi przykład nadal może pozostawić otwarte połączenie, jeśli wystąpi wyjątek. Nie tylko za pomocą wywołania Usuwaj, gdy wykonywanie opuszcza ten blok, ale także wywołuje go, gdy wystąpi wyjątek. – ThatBlairGuy

3

Nie, użycie tworzy pewne konkretne konstrukcje porządkowe, których nie można uzyskać za pomocą nawiasów klamrowych.

Jeśli używasz reflektor i spojrzeć na IL tam wezwanie do dysponowania dodany na końcu przy użyciu bloku na jego docelowa:

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 

To wyjaśnia, dlaczego nie można utworzyć nowego połączenia wewnątrz za pomocą bloku i przypisać go do zmiennej - spowoduje to, że oryginalny odnośnik będzie zawieszony i nieusuwalny.

+1

Myślę, że to wywołuje Dispose() na IDisposable obiektów ... nie wiem na pewno –

+1

@Mike - Możesz używać tylko 'using' na IDisposable obiektów, ponieważ wywołuje Dispose() – annakata

+3

To faktycznie wywołuje' ((IDisposable) blah) .Dispose() ', gdzie' blah' jest zmienną używaną. Nie kompiluje się, jeśli typ nie implementuje 'IDisposable'. – Timwi

3

Nie, nie zostanie wywołany po zamkniętym nawiasie klamrowym. Musisz wywołać to ręcznie lub użyć instrukcji using.

Jeśli nie wykonasz rozładunku, zostanie ono wykonane po wywołaniu finalizacji. I tutaj masz 2 problemy:

  • Wykonanie metody Finalize jest kosztowne dla wydajności. Jeśli twoja metoda Dispose już wykonała zadanie oczyszczenia obiektu, to nie jest konieczne, aby garbage collect wywoływał metodę Finalize obiektu (jeśli jest dobrze zaimplementowana, metoda Dispose powinna wywołać metodę SuppressFinalize dla obiektu, który jest wyrzucany). (MSDN)
  • Nie kontrolujesz momentu, w którym finalizacja jest automatycznie wywoływana i nie może zostać wykonana (z powodu awarii na przykład).
8

Oświadczenie using jest cukier syntaktyczny, który wywołuje Dispose na obiektach zainicjowane w (), więc nie można po prostu zastąpić go jak masz na swoim przykładzie.

Należy zauważyć, że jedynymi obiektami, których można użyć w ramach instrukcji using są te, które implementują IDisposable, co zapewnia, że ​​można wywołać Dispose.

Jak this artykule wyjaśniono, kompilator przekształci to:

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

} 

do tego:

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

Więc, chyba że duplikat tej struktury, nie będzie uzyskać takie samo zachowanie.

Ponadto - ponowne użycie zmiennej, tak jak w twoim przykładzie, jest złą praktyką.Ktoś, kto czyta kod, może myśleć, że patrzy na połączenie 1, gdy w rzeczywistości patrzy na 2 lub 3. Może to być bardzo mylące i powodować różnego rodzaju problemy na drodze.

+0

Cóż, próbowałem pozbyć się użytków, ponieważ mam ich dużo (często używam ich również z "strumieniami" w moim projekcie). Drugim powodem jest to, że mam wiele połączeń sekwencyjnych, więc pomyślałem o użyciu jednego obiektu. Chciałem poprawić czytelność, ale masz rację, może po prostu ją zmniejszyć. – nan

3
using(foo) 
{ 
    // stuff 
} 

... jest trochę cukru, który przekłada się na:

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

co następuje:

{ 
    // stuff 
} 

... nie przekłada się na nic. To jest po prostu:

{ 
    // stuff 
} 

: D

Edycja:.. Proszę nie zniszczyć formatowania podczas edycji :(

2

Nie, nie możesz tego zrobić, przynajmniej w C#

Ale można tworzyć różne przedmioty jednorazowego użytku wewnątrz jednego pomocą instrukcji:

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

C++/CLI można używać klas jednorazowych w stos podobny 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 dla miłego grupowania użytkowników, nie wiedziałem o tym – nan

1

myślałem, że to zbyt najpierw ... ale widocznie kiedy wykorzystujące końce blokowe, .Dispose() nazywany jest na obiekt, który jest inny niż pozwolenie, aby przedmiot wykracza poza zakres. Ponieważ C# to język gromadzący śmieci, może upłynąć trochę czasu, zanim obiekt zostanie faktycznie wyczyszczony. Blok using zapewnia, że ​​jest on usuwany natychmiast i niezależnie od wyjątków.

Powiązane problemy