使用语句可以被花括号替代吗?

6
我使用 using 语句来调用 SqlConnection。这样可以提高性能,因为它会强制调用 Dispose() 来更早地释放连接到连接池中。
然而,我意识到使用 using 创建的对象无法重新定义。我无法像下面这样做:
   using (SqlConnection connection = new SqlConnection(connectionString))
   {
       connection.Open();
       //...
       connection = new SqlConnection(connectionString2);
       //...
       connection = new SqlConnection(connectionString3);
   }

我在想是否可以替换 "using",并像下面这样做:

我在想是否可以使用如下方式进行替换:

 {
       SqlConnection connection = new SqlConnection(connectionString);

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

SqlConnection在最后一个}大括号之后将不可访问。对象超出其作用域时,Dispose()会被立即调用吗?

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

谢谢。当涉及到关闭连接时,我当然会调用connection.Close(),但我上面的代码没有写。 - nan
1
即使您明确调用Close(),如果发生异常,您的第二个示例仍有可能保留连接。使用Dispose不仅在执行离开该块时调用它,而且在发生异常时也会调用它。 - ThatBlairGuy

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。这可能会导致非常混乱,并在未来引起各种问题。


我试图摆脱“usings”因为我有很多它们(我在项目中还经常使用它们与“Streams”配合使用)。第二个原因是我有很多顺序连接,所以我想使用一个对象。我想提高可读性,但你说得对,这可能会降低可读性。 - nan

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块内创建新的连接并将其分配给变量--这样做会使原始引用悬空且未处理。

1
我认为它在IDisposable对象上调用Dispose()...但不确定。 - Mike Gleason jr Couturier
1
@Mike - 你只能在IDisposable对象上使用 using,因为它会调用Dispose()方法。 - annakata
3
实际上它调用的是((IDisposable) blah).Dispose(),其中blah是使用变量。如果类型没有实现IDisposable,则代码无法编译通过。 - Timwi
感谢您的澄清! - Mike Gleason jr Couturier

3
using(foo)
{
  // stuff
}

这是一点糖,意思是:
try
{
  // stuff
}
finally
{
  foo.Dispose()
}

其中:
{
  // stuff
}

这句话的意思是“...没有任何翻译。它只是:”。
{
  // stuff
}

:D

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


3
不,它不会在闭合的大括号后自动调用。您需要手动调用它或使用 using 语句。
如果您不执行 dispose,则在调用 finalize 时将执行它。这里有两个问题:
- 执行 finalize 方法会影响性能。如果您的 Dispose 方法已经完成了清理对象的工作,则垃圾回收器不必调用对象的 Finalize 方法(如果实现得好,Dispose 方法应该为正在处理的对象调用 SuppressFinalize 方法)。(MSDN) - 您无法控制 finalize 自动调用的时间,并且可能无法执行(例如,由于崩溃)。

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

}

1
一开始我也是这么想的...但显然当using块结束时,将调用 .Dispose()方法释放你的对象,这与让对象超出范围不同。因为C#是一种垃圾收集语言,在实际清理对象之前可能需要一些时间。 using 块确保对象立即被处理,并且不受任何异常的影响。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接