"using"语句与花括号的区别

10

我想知道为什么在C#中要使用using语句。我查了一下,发现它用于执行语句,然后清理对象。所以我的问题是:如果我们打开和关闭花括号({ })来定义作用域,这不是同样的事情吗?

Using语句:

using (SqlConnection conn = new SqlConnection(connString)) {
     SqlCommand cmd = conn.CreateCommand();
     cmd.CommandText = "SELECT * FROM Customers";
     conn.Open();
     using (SqlDataReader dr = cmd.ExecuteReader()) {
          while (dr.Read()) 
          // Do Something...
     }
}

花括号:

{
     SqlConnection conn = new SqlConnection(connString);
     SqlCommand cmd = conn.CreateCommand();
     cmd.CommandText = "SELECT * FROM Customers";
     conn.Open();
     {
          SqlDataReader dr = cmd.ExecuteReader();
          while (dr.Read()) 
          // Do Something...
     }
}

这两种方法有明显的区别吗?


2
这与 c++ 有什么关系? - BoBTFish
花括号在C++中的使用方式与本问题中的使用方式相同,这就是我混淆的原因。抱歉。 - hevele
6个回答

6

嗯,使用(仅在类实现IDisposable接口时才合法)

using (SqlConnection conn = new SqlConnection(connString)) {
  // Some Code
  ...
}

等于这段代码

SqlConnection conn = null;

try {
  SqlConnection conn = new SqlConnection(connString);

  // Some Code
  ...
}
finally {
  if (!Object.ReferenceEquals(null, conn))
    conn.Dispose();
}

C#和C++在这方面的行为不同,因此在C#中不要像在C++中一样使用{...}模式:

{
  SqlConnection conn = new SqlConnection(connString);
  ...
  // Here at {...} block exit system's behavior is quite different:
  //
  // C++: conn destructor will be called, 
  // resources (db connection) will be safely freed
  //
  // C#:  nothing will have happened! 
  // Sometimes later on (if only!) GC (garbage collector) 
  // will collect conn istance and free resources (db connection). 
  // So, in case of C#, we have a resource leak 
}

那么使用 using 还是 try/finally 实际上并不重要吗?另一个问题是,据我回忆,我们也可以在 try 和 finally 之间使用 catch。如果我们想对应该 catch 块,using 语句的哪个部分涵盖了它? - hevele
1
是的,你说得很对:using只是一种语法糖。如果你把catch()放到同一个try {...}中与finally{}一起使用,它将对应于在using() {...}内部的try {...} catch() {...}:using (...) {try {...} catch() {...}} - Dmitry Bychenko

3

花括号只是表示一段代码块的标志。

而使用“Using”指示GC进行处理。

任何实现IDisposable接口的类都可以与Using一起使用。

使用将基本上转换为(请注意,没有catch)。如果有任何异常,它将被抛出。但即使如此,连接也会被释放。

SqlConnection conn;
try
{
    conn = new SqlConnection(connString);
}
finally
{
    if (conn != null)
        conn.Dispose();
}

1

When you use

using( ...){
... code
}

这实际上是使用模式,C#编译器会生成代码来调用在using块中创建的对象实现的dispose方法。

因此,对于任何实现IDisposable接口的对象,您都可以使用

using(var disposableObject = new DisposableObject()){
}

当编译器编译时,将生成以下代码。
DisposableObject disposableObject = null;
try
{
   disposableObject = new DisposableObject();
}
finally{
   disposableObject.Dispose();
}

使用 (..) 语句可以确保即使在该语句内部抛出异常,也会调用可释放对象的 dispose 方法。

1
命名空间改变名称查找,而花括号创建一个新的堆栈,其中可以创建局部变量。

那么使用不会创建新的堆栈来查找局部变量?这是正确的吗? - hevele
@Mert Toka:我原以为它涉及C ++。现在我看到它已经被重新标记为C#了。 - user2672165

1
如果您使用带有键 using 的版本,则.NET平台会运行方法 Dispose 以释放对象使用的资源。因此,对象必须实现IDisposable接口。通过这种方式,您可以以确定性的方式释放资源(不像垃圾回收器那样)。
强烈建议在方法Dispose中清理非托管资源,因为GC不会对其进行清理。
花括号只确定其中使用的变量的作用域,但在运行时到达关闭括号后不会清除资源。GC根据其算法以非确定性的方式执行此操作。

0

使用等同于以下

SqlConnection conn;
try
{
   conn = new SqlConnection(connString)
}

finally
{
  conn.Dispose() //using does this automatically for you
}

规则是当某个类实现了 IDisposable 接口时,可以使用 Using 块代替 try catch, finally 模式。
如果你想更深入地了解GC的工作原理,请阅读Jeffery Richter的这篇优秀文章。

是的,catch不会出现。只会有try和finally。 - Anand
它实际上替换了Try/Catch吗?它只替换了对Dispose显式调用? - Christian Phillips
捕获块怎么办?我的意思是,using 块的哪一部分实际上对应于捕获块? - hevele
1
代码行之间并不存在一对一的映射关系。因此,整个 try 和 finally 代码块等同于 Using 代码块。 - Anand
从技术上讲,如果 SqlConnection 构造函数中抛出异常(例如,如果 connString 错误),则 conn 局部变量将是未定义的(它将包含垃圾值),并且您的代码将在“finally”块中崩溃。要重现“using”的效果,应该在“try”之前添加“SqlConnection conn = null;”。 - Dmitry Bychenko
赋值应该在 try 块之外。 - klumsy

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