在C#中什么时候应该使用"using"块?

75

在使用“using”块时,有哪些特定情况应该(或不应该)使用:

using(SomeType t = new SomeType()){
    ...
}

4
为什么这是一个社区维基? - JaredPar
1
@JaredPar,我也不明白。无论如何,这是他选择不分配声望的决定。 =[ - strager
重复的问题,已经被问过很多次了。 - George Stocker
1
@JaredPar;有争议和主观性。从人到人之间使用using声明的方式似乎存在很大差异。 - jay_t55
14个回答

107
有些对象需要在使用完后进行一些操作。通常这是因为对象使用了一些需要释放的资源。例如,如果您有一个类为File的文件对象,并且此对象打开了来自文件系统的文件,则文件系统中的文件将需要关闭。
如果您只是保留文件对象并忘记调用file.Close(),则它将不会被清理,直到垃圾回收器(GC)运行并发现没有任何东西仍在使用该文件对象。垃圾回收器的运行应由公共语言运行时(CLR)决定。如果垃圾回收器在您完成文件处理后很长时间才运行,则文件可能会一直保持打开状态。如果有许多文件对象,或者如果某些东西想要打开一个文件,但由于您保留的文件对象仍在继续,导致无法打开文件,这可能会带来大问题。
为解决此问题,C#引入了IDisposable接口。这个接口有一个称为Dispose的方法。需要进行一些清理的类实现了Dispose方法。这为您提供了一种标准的方式来清理使用资源的任何对象。有许多需要调用Dispose的类。这个问题在于代码被覆盖了许多Dispose调用,而且它们很难跟踪,因为您创建对象和调用Dispose以清理它的位置不同。所以,您需要仔细查看代码并仔细检查是否在正确的位置调用了Dispose。
为解决这个问题,C#引入了'using'关键字。您可以在new一个对象的地方放置'using'关键字,这将确保Dispose将自动调用它。它保证无论发生什么事情,甚至如果在using语句的主体内部抛出异常,Dispose都会被调用。
因此,当您想要确保分配资源的对象被清理时,应使用'using'。
注意,'using'只能用于在函数中声明的堆栈对象。它不适用于作为类成员声明的对象。对于它们,您必须自己调用Dispose。您可能需要在您的类中实现Dispose,以便调用那些需要它的成员对象的Dispose。
通常需要调用using的常见对象有:文件,数据库连接,如Pen和Brush的图形对象。
有时,当您想要同时执行两个操作时也会使用'using'。例如,如果您希望在进入和退出代码块时编写日志记录语句,则可以编写一个Log类,您可以像这样使用它:
using( Log log = new Log("Doing stuff") )
{
    // Stuff
}

可以将log类的构造函数修改为输出消息,同时Dispose方法也要输出消息。实现finalizer(~Log),如果没有调用Dispose方法则会断言以确保“using”记住了“new Log”。


8
很好的总结!+1 - Nikita Ignatov
1
同意。这是一个很好的总结。 - Johnny Oshika

97

12
使用被翻译为: SomeType t = new SomeType(); 尝试 { ... } 最后 { t.Dispose(); } - ConsultUtah
4
实现了 IDisposable 接口的类型,是否存在不应使用 using 模式的情况? - Matthew
6
事实上,确实是这样的——特别是当你在处理WCF代理时。其中一些问题可以在这里找到:http://msdn.microsoft.com/en-us/library/aa355056.aspx - Otávio Décio
4
@Matthew,当将using与任何异步操作关联时,你需要谨慎行事:http://softwareblog.alcedo.com/post/2011/12/09/Using-blocks-and-asynchronous-operations.aspx - Benjol
请查看使用:一个神奇的C#关键字 - Sachin Dhir

13

如果类型实现了IDisposable,则使用using,除非你打算无论如何都要用try/catch块来包装它,那么你也可以(根据你喜欢的外观)使用finally块。


1
即使使用finally,我认为“using”更可取。当您在“using”中看到一个新对象时,您知道它的处理已经被处理了。如果没有“using”,您必须滚动并了解控制流以确信没有泄漏。这需要不必要的努力。 - Scott Langham

11

我看到许多其他答案表明当你应该使用using语句时。我想解决的是,什么情况下不应该使用using语句:

如果你需要在当前函数的范围之外使用对象,则不要使用using块。好的例子是返回数据库连接的工厂方法或需要返回数据读取器的方法。在这两种情况下,如果你使用using语句创建对象,则它将在方法返回之前被处理,因此无法在方法外使用。

现在,你仍然希望确保这些对象被处理,因此你可能仍然需要在某个地方使用using语句。只是不要把它包含在实际创建对象的方法中。相反,可以在函数调用本身中包装一个using语句。


4

例子:

        using(SqlConnection MyConnection = new SqlConnection("Connection string"))
        {
            MyConnection.Open();

            //...

            // 1. SQLConnection is a type that implements IDisposable
            // 2. So you can use MyConnection in a using statement
            // 3. When using block finishes, it calls Dispose method of 
            // SqlConnection class
            // 4. In this case, it will probably close the connection to 
            // the database and dispose MyConnection object

        }

您可以创建自己的对象来实现IDisposable:

public class MyOwnObjectThatImplementsIDisposable : IDisposable
{

    //... some code

    public void Dispose()
    {
        // Put here the code you want to be executed when the
        // using statement finish.
    }
}

因此,您可以在using语句中使用一个MyOwnObjectThanImplementsIDisposable类型的对象:

        using(MyOwnObjectThatImplementsIDisposable MyObject = new MyOwnObjectThatImplementsIDisposable)
        {

            // When the statement finishes, it calls the 
            // code you´ve writed in Dispose method
            // of MyOwnObjectThatImplementsIDisposable class
        }

希望这可以帮到你。

4

当SomeType实现IDisposable接口时。

这提示开发人员SomeType使用需要清理的非托管资源。


2

如果您需要一个总结规则,那么在使用IDisposable的对象且没有catch语句时,请使用using。实际上,using是这种模式:

try
{
  //instantiate and use object
}
finally
{
  //dispose object
}

如果您不需要捕获异常,使用try/except可以减少输入,这是一件好事。


2
在这个情况下,using语句对于实现IDisposable接口的类型非常方便。当代码块退出using语句的作用域时,Dispose()会被隐式调用。在使用后立即释放对象时,这是一个好习惯。

2

在使用using块时需要特别小心的一种情况是使用WCF服务客户端

正如MSDN文章中所述,将WCF客户端(实现了IDisposable)包装在using块中可能会掩盖任何导致客户端处于故障状态的错误(例如超时或通信问题)。简而言之,当调用Dispose()时,客户端的Close()方法会触发,但由于它处于故障状态,会抛出错误。原始异常随后被第二个异常掩盖。这不好。

有各种解决方法,包括MSDN文章本身提供的方法。其他方法可以在IServiceOrientedblog.davidbarret.net上找到。

我自己更喜欢最后一种方法。


哇,那真的很可怕。我只是在需要打开连接时使用“using”语句。 - Alex

1
主要规则是: * 当对象实现IDisposable接口时,请使用USING语句。

该接口提供了Dispose方法,它应该释放对象的资源。如果不调用此方法,则对象将一直停留在内存中,直到CLR想要执行垃圾回收为止。如果程序员使用USING语句,则在结束时对象将被处理,并且所有资源将被释放。

非常重要的是,所有不再使用的资源尽快释放。

有关更多信息,请访问以下链接:microsoft


可以再表达得更清楚一些,但这不值得投反对票。 - Allen Rice

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