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() 吗?

4

7 回答 7

13

不,在您的第二个示例中,事情不会自动清理(实际上,使用您拥有的代码,您将保持几个连接处于打开状态)。

不仅如此,如果在using块内抛出异常,您将失去自动清理。请记住,一个using块分解为:

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

如果你真的在使用不同的连接并且每个连接都在块的末尾被处理,我会使用几个 using 块:

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
于 2010-09-03T12:55:00.540 回答
8

using语句是调用Dispose在 中初始化的对象的语法糖(),因此您不能像在示例中那样简单地替换它。

您会注意到,您可以在using语句中使用的唯一对象是那些实现IDisposable,这确保Dispose可以调用。

正如本文所解释的,编译器将对此进行转换:

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

}

对此:

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

所以,除非你复制这个结构,否则你不会得到相同的行为。

此外 - 以您在示例中的方式重用变量是不好的做法。阅读代码的人可能会认为他们正在查看连接 1,而实际上他们正在查看连接 2 或 3。最终可能会非常混乱并导致各种问题。

于 2010-09-03T12:54:17.103 回答
3

不, using 创建了一些特定的清理结构,你不会只使用大括号。

如果您使用 Reflector 并查看 IL,则会在 using 块的末尾为其目标添加对 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 

这解释了为什么您不能在 using 块内创建新连接并将其分配给变量 - 这样做会使原始引用挂起并且未处理。

于 2010-09-03T12:51:30.250 回答
3

不,它不会在右花括号之后被调用。您需要手动调用它或使用using语句。

如果你不执行 dispose 那么它将在调用 finalize 时执行。在这里你有两个问题

  • 执行 Finalize 方法的性能代价很高。如果你的 Dispose 方法已经完成了清理对象的工作,那么垃圾收集器就没有必要调用对象的 Finalize 方法(如果实现得很好,Dispose 方法应该为它正在处理的对象调用 SuppressFinalize 方法)。 (MSDN)
  • 您无法控制自动调用 finalize 并且无法执行(例如因为崩溃)的时刻。
于 2010-09-03T12:51:40.230 回答
3
using(foo)
{
  // stuff
}

...是一点糖,可以转化为:

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

然而:

{
  // stuff
}

...不会转化为任何东西。只是:

{
  // stuff
}

:D

编辑:编辑时请不要破坏格式:(

于 2010-09-03T12:57:27.853 回答
2

不,你不能这样做。至少在 C# 中。

但是您可以在一个 using 语句中创建不同的一次性对象:

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

C++/CLI 你可以在类似堆栈的方式中使用你的一次性类:

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();
  ...
}

}
于 2010-09-03T13:35:03.003 回答
1

一开始我也这么认为......但显然当 using 块结束时,.Dispose()会在您的对象上调用,这与让对象超出范围不同。由于 C# 是一种垃圾收集语言,因此实际清理对象可能需要一些时间。该using块确保它被立即处理,并且不考虑任何异常。

于 2010-09-06T08:06:44.933 回答