73

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

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

14 回答 14

101

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

如果您只是离开了文件对象,而忘记调用 file.Close() 它不会被清理,直到垃圾收集器 (GC) 运行并发现没有任何东西仍在使用文件对象。垃圾收集器何时运行应该留给公共语言运行时 (CLR) 来决定。如果在您处理完文件后 GC 有一段时间没有运行,则该文件可能会长时间保持打开状态。如果有很多文件对象,或者如果有东西想打开一个文件,但因为你留下的文件对象还在附近,这可能会造成很大的问题。

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

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

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


using 只能用于在堆栈上声明的对象,即在函数中。它不适用于声明为类成员的对象。对于他们,您必须自己调用 Dispose。您可能必须在您的类中实现 Dispose,以便在它拥有的任何需要它的成员对象上调用 Dispose。


需要使用的常见对象有:文件、数据库连接、Pen 和 Brush 等图形对象。


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

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

可以让日志类的构造函数写出消息,Dispose方法也可以写出消息。实现终结器 (~Log) 以断言是否未调用 Dispose 方法以确保在“新日志”周围记住“使用”。

于 2009-02-19T21:47:33.820 回答
95

SomeType类实现IDisposable.

于 2009-02-19T21:04:45.237 回答
13

using只要类型 implements就使用IDisposable,除非您无论如何要将它包装在try/catch块中,那么您也可以(取决于您喜欢的外观)使用finally块。

于 2009-02-19T21:05:12.570 回答
11

当您应该发表using声明时,我看到许多其他答案。我想解决什么时候特别不应该一个using声明:

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

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

于 2009-02-19T21:48:36.970 回答
4

当 SomeType 实现 IDisposable 时。

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

于 2009-02-19T21:05:21.367 回答
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
        }

希望这可以帮助

于 2009-02-19T21:21:59.040 回答
2

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

于 2009-02-19T21:05:46.983 回答
2

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

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

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

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

于 2009-02-19T23:16:04.527 回答
2

如果你想要一个摘要规则。任何时候使用 IDisposable 的对象,如果您没有捕获,请使用 using。本质上,使用的是这种模式:

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

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

于 2009-02-20T03:56:59.547 回答
1

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

该接口提供了 Dispose 方法,该方法应该释放对象的资源。如果不调用此方法,则对象将一直保留在内存中,因为 CLR 想要执行垃圾回收。如果程序员使用 USING 语句,那么最后对象将被释放,所有资源都将被释放。

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

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

于 2009-02-19T21:14:53.940 回答
1

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

于 2009-02-19T22:48:14.220 回答
0

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

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

于 2009-02-19T21:06:27.433 回答
0

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

但是使用“using”语句时的警告之一是,即使“SomeType”无论如何都会被处理,“using”中抛出的任何异常都不会被捕获。

所以在下面的片段中,

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

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

于 2009-02-19T21:15:55.513 回答
0

如果某些东西实现了,并且如果你想要处理的东西保留在非托管资源(如数据库连接和文件句柄)上, 我还会添加一条using()语句。IDispose

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

于 2009-02-19T23:00:43.490 回答