2010-09-03 2 views
6

Я использую оператор using для SqlConnection. Это хорошо для производительности, потому что силы, вызывающие Dispose(), просто освобождают соединение с пулом раньше.Может ли оператор using заменить фигурные скобки?

Однако я понял, что объект, созданный при использовании, не может быть переопределен. Я не могу сделать так:

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

мне было интересно, если я могу заменить, используя, и сделать что-то вроде этого:

{ 
     SqlConnection connection = new SqlConnection(connectionString); 

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

SqlConnection не будет после того, как последний подъездом } скобки. Будет ли Dispose() вызывается немедленно, когда объект выходит из области видимости?

ответ

13

Нет, вещи не будут автоматически очищены в вашем втором примере (фактически, с кодом, который у вас есть, вы оставите несколько подключений открытыми).

Не только это, но вы теряете автоматическую очистку в случае исключения Исключений внутри блока using. Помните, что using блок распадается на:

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

Если вы действительно используете разные соединения и каждое соединение удалялись в конце блока, я хотел бы использовать несколько блоков с использованием:

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

Спасибо. Если дело доходит до закрытия соединений, я бы, конечно, вызывал connection.Close(), но я не писал его в коде выше. – nan

+1

Даже если вы явно вызываете Close(), ваш второй пример по-прежнему может оставить открытое соединение, если возникло исключение. Не только использование вызова Dispose, когда выполнение покидает этот блок, оно также вызывает его при возникновении исключения. – ThatBlairGuy

3

Нет, использование создает некоторые конкретные конструкции очистки, которые вы не получите только с фигурными скобками.

Если вы используете отражатель и посмотреть на IL есть вызов Dispose добавил в конце, используя блок для своей цели:

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 

Это объясняет, почему вы не можете создать новое соединение внутри используя блок и назначить его переменной - это приведет к тому, что исходная ссылка будет висит и не будет отображаться.

+1

Я думаю, что он вызывает Dispose() на объектах IDisposable ... не знаю точно –

+1

@Mike - вы можете использовать 'using' только для IDisposable-объектов, потому что он вызывает Dispose() – annakata

+3

Фактически он вызывает' ((IDisposable) blah) .Dispose() ', где' blah' - используемая переменная. Он не компилируется, если тип не реализует 'IDisposable'. – Timwi

3

Нет, он не будет вызываться после закрытия фигурной скобки. Вам нужно вызвать его вручную или использовать оператор using.

Если вы не выполните команду dispose, то она будет выполнена при вызове finalize. И здесь у вас есть 2 проблемы

  • Выполнение метода Finalize является дорогостоящим для производительности. Если ваш метод Dispose уже выполнил работу по очистке объекта, тогда сборщик мусора не должен вызывать метод Finalize объекта (если он хорошо реализован, метод Dispose должен вызывать метод SuppressFinalize для объекта, который он утилизирует). (MSDN)
  • Вы не контролируете момент, когда финализация автоматически вызывается и не может быть выполнена (например, при сбое).
8

using заявление синтаксический сахар, который вызывает Dispose на объекты инициализируются в (), так что вы не можете просто заменить его, как у вас есть в вашем примере.

Вы заметите, что единственными объектами, которые вы можете использовать в операторе using, являются те, которые реализуют IDisposable, что гарантирует, что можно вызвать Dispose.

Как объясняет this статья, компилятор преобразует это:

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

} 

Для этого:

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

Таким образом, если вы не дублировать эту структуру, вы не получите такое же поведение.

Кроме того, повторное использование переменной, как вы в своем примере, является плохой практикой.Кто-то, кто читает код, может подумать, что они смотрят на соединение 1, когда они действительно смотрят на 2 или 3. Может оказаться очень запутанным и вызвать всевозможные проблемы в будущем.

+0

Ну, я пытался избавиться от привычек, потому что у меня их много (я также часто их использую с Streams в моем проекте). Вторая причина заключается в том, что у меня много последовательных соединений, поэтому я думал об использовании одного объекта. Я хотел увеличить удобочитаемость, но вы правы, это может просто уменьшить его. – nan

3
using(foo) 
{ 
    // stuff 
} 

... немного сахара, который выливается:

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

Принимая во внимание:

{ 
    // stuff 
} 

... не переводится ни во что. Это просто:

{ 
    // stuff 
} 

: D

Edit:.. Пожалуйста, не уничтожить форматирование при редактировании :(

2

Нет, вы не можете сделать это по крайней мере в C#

Но вы можете создавать различные одноразовые предметы внутри одного используя оператор:

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

C++/CLI вы можете использовать свои одноразовые классы в стеке, как 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 для приятной группировки «usings», не знал об этом – nan

1

Я думал, это тоже на первый ... но, по-видимому, когда с помощью Концы блоков, .Dispose() вызывается для объекта, который чем позволить объекту выйти из сферы действия. Поскольку C# - это сборщик мусора, это может занять некоторое время, прежде чем ваш объект будет фактически очищен. Блок using обеспечивает его немедленное удаление и независимо от каких-либо исключений.

Смежные вопросы