我什么时候应该在C#中使用“使用”块?

时间:2009-02-19 21:02:08

标签: c# .net using

是否有特殊情况我应该(或不应该?)使用“使用”块:

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

14 个答案:

答案 0 :(得分:90)

SomeType类实现IDisposable时。

答案 1 :(得分:89)

有些对象在完成它们后需要采取一些操作。通常这是因为对象使用某种需要处理的资源。例如,如果您有一个File类的文件对象,并且该对象从文件系统打开一个文件,则需要再次关闭文件系统中的文件。

如果你刚离开文件对象,忘了调用file.Close(),那么在垃圾收集器(GC)运行并且没有任何东西仍在使用文件对象之前,它将不会被清除。当垃圾收集器运行时应留给公共语言运行时(CLR)来决定。如果在完成文件后GC没有运行很长时间,则文件可能会长时间保持打开状态。如果有很多文件对象,或者某些东西想要打开文件,这可能会造成很大的问题,但是因为你离开的文件对象仍然存在而无法解决这个问题。

要解决此问题,C#具有IDisposable接口。这有一个名为Dispose的方法。需要一些清理的类实现此Dispose方法。这为清理任何使用资源的对象提供了标准方法。有许多类需要调用Dispose。这个问题是代码被Dispose调用覆盖,并且它们很难跟随,因为你新建对象并调用Dispose来清理它的地方是不同的。因此,您必须仔细查看代码并非常小心地检查是否在正确的位置调用了Dispose。

为了解决这个问题,C#引入了'using'关键字。您可以在新对象的位置放置一个“using”关键字,这样可以确保为您调用Dispose。它保证无论发生什么事都会调用Dispose ...即使在using语句的主体内抛出异常。

因此,当您想要确保分配资源的对象将被清理时,您应该使用'using'。


using只能用于在堆栈中声明的对象,即在函数中。它不适用于声明为类成员的对象。对他们来说,你必须打电话给自己。您可能必须在类中实现Dispose,以便in可以调用它所需的任何成员对象上的Dispose。


需要使用它们的公共对象是:文件,数据库连接,图形对象,如笔和画笔。


有时,当您希望两个操作一起发生时,也会使用它。例如,如果您想在输入代码块时写入日志语句,并且在退出时您可以编写一个可以使用的日志类,如下所示:

using( Log log = new Log("Doing stuff") )
{
    // Stuff
}

可以使用日志类的构造函数来写出消息,并且Dispose方法也可以将其写出来。实现终结器(~Log)以断言是否未调用Dispose方法以确保在'new Log'周围记住'using'。

答案 2 :(得分:13)

只要类型实现using,就使用IDisposable,除非你打算将它包装在try/catch块中,然后你也可以(根据你喜欢的样子)使用finally块。

答案 3 :(得分:12)

应该using语句时,我会看到很多其他答案。我想在具有using语句时明确说明:

如果您需要使用当前函数范围之外的对象,请不要使用using块。很好的例子是返回数据库连接的工厂方法或需要返回datareader的方法。在任何一种情况下,如果使用using语句创建对象,它将在返回方法之前处理,因此无法在方法之外使用。

现在,您仍然希望确定这些对象已被处置,因此您仍可能需要某处using语句。只是不要将它包含在实际创建对象的方法中。相反,您可以将函数调用本身包装在using语句中。

答案 4 :(得分:4)

当SomeType实现IDisposable时。

这是开发人员的一个线索,SomeType使用需要清理的非托管资源。

答案 5 :(得分: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
        }

希望这有帮助

答案 6 :(得分:2)

在此上下文中,using语句对于实现IDisposable的类型很方便。当代码块退出using语句的范围时,将隐式调用Dispose()。在使用后想要立即处理的物体时,这是一个好习惯。

答案 7 :(得分:2)

使用using块时应注意的一个特定实例是使用WCF服务客户端

this MSDN article所述,在IDisposable块中包装WCF客户端(实现using)可能会掩盖导致客户端处于故障状态的任何错误(像超时或通信问题)。长话短说,当调用Dispose()时,客户端的Close()方法会触发,但会抛出错误,因为它处于故障状态。然后,第二个异常掩盖了原始异常。不好。

有各种解决方法,包括MSDN文章中的一个。其他人可以在IServiceOrientedblog.davidbarret.net找到。

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

答案 8 :(得分:2)

如果您需要摘要规则。任何时候使用IDisposable的对象都没有捕获,请使用。基本上,使用这种模式:

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

如果您不需要捕获,使用可以节省您的输入,这是一件好事。

答案 9 :(得分:1)

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

此接口提供Dispose方法,该方法应释放对象的资源。如果未调用此方法,则对象将保留在内存中,因为CLR希望执行垃圾回收。如果程序员使用USING语句,那么最后将处理该对象,并且所有资源都是免费的。

非常重要的是,所有不再使用的资源都应尽快免费使用。

有关它的更多信息,请访问此链接:microsoft

答案 10 :(得分:1)

也许值得一提的是,添加“使用”lo C#语言的根本原因如下:某些资源可能非常缺乏,等待GC调用IDisposable是没有意义的。例如,DB连接。如果你使用try / catch / finally,你最终不会有悬空连接,但连接将保持挂起,直到GC没有启动,这可能需要一段时间(如果你没有明确关闭它)。如果您使用“使用”(原谅双关语),即使您忘记关闭它,即使在使用块内发生了一些异常,您也会立即释放连接。
正如之前的帖子所提到的,另一个原因是程序员并不总是最终使用来清理。如果在异常的情况下不使用finally,则最终会泄漏资源......

答案 11 :(得分:0)

有一种情况是你想在代码块的开头做一些事情,然后无条件地在块的末尾撤消它(即使有一个抛出)。

您构建的一次性类的ctor(以及在使用中调用)将执行操作,然后Dispose方法将撤消该操作。这通常是我使用它的方式。

答案 12 :(得分:0)

其他人已经提到过“IDisposable”。

但使用“使用”声明时的一个警告是, “使用”中抛出的任何异常都不会被捕获 甚至认为“SomeType”会被处理掉。

所以在下面的代码片段中,

using (SomeType t = new SomeType()){
    throw new Exception("thrown within using");
}

throw new Exception("thrown within using");不应该被忽视。

答案 13 :(得分:0)

我还要补充一点,如果某些东西实现了using(),那么使用IDispose语句,如果你想要处理的东西保留在数据库连接和文件句柄等非管理资源上。

如果它是一个说List<T>的普通对象,其中T就像一个包含名称和地址的Customer对象,那么你不需要。垃圾收集器足够聪明,可以为您管理。但垃圾收集器不会返回连接池或关闭文件句柄的连接。