IDisposable接口如何工作?

时间:2009-03-10 19:55:00

标签: .net garbage-collection dispose idisposable

我知道它用于释放非托管资源,但是,我对实际调用Dispose时感到困惑。我知道它在using块的末尾被调用,但是当对象被垃圾收集时它是否也会被调用?

5 个答案:

答案 0 :(得分:11)

如果你正确实现了IDisposable,你还应该包含一个终结器,它将在你的对象上调用Dispose()。

如果你这样做,它将被GC调用。但是,尝试始终自行处理这些对象仍然是一个非常好的主意。

依赖终结器来调用Dispose的最大问题是它将发生在你无法控制的另一个线程中。在某些情况下,这可能会产生令人讨厌的后果,包括导致GC线程中发生的异常,这是不好的,以及您检查的处理字段。这也是为什么在Dispose()方法中包含GC.SuppressFinalize(this)很重要的原因 - 一旦对象被处理掉,你就不想重新处理它了。

答案 1 :(得分:3)

在几个地方调用Dispose:

  1. 在使用区块的末尾。
  2. 明确调用时(例如,在try {} finally {}中。)
  3. 建议您在完成资源后自行调用,以便更好地管理资源。

    编辑:我错了。垃圾收集期间不调用Dispose。请参阅this文章。

答案 2 :(得分:2)

在Using块的末尾调用Dispose(),这样当对象超出范围时,您可以指望Dispose操作(例如关闭数据库连接)。就这么简单。

更新:在调用Dispose时,没有任何特定于非托管资源的内容(尽管这是一种常见用法)。

更新2 :关于Reed Copsey启动的线程存在一些争论,这对于理解IDisposable非常有用。对于想要了解更多信息的人,我强烈推荐this article

简而言之,IDisposable类允许您通过Dispose()方法显式处理资源的释放(通常是非托管资源或数据库连接)。应在“使用”块中创建IDisposable类实例,以确保实际调用Dispose方法。如果你没有这样做(或者在“finally”块中明确地调用它等),那么你的Dispose方法将不会被调用,你将孤立你要清理的对象。在所有的情况下,我将Disposable类放在Using blocks中,你也应该这样做。

作为替代方案,您可以在Finalizer(类Destructor)中处理资源的清理。当类为GC时,将自动调用此方法。这种方法的缺点是,当清除对象并且存在一些需要解决的线程问题时,您将不会显式控制。因此,例如,析构函数中的问题很难调试,因为它们是异步调用并且在不同的线程中调用。唯一的好处是你不必记得像处理Dispose一样调用你的析构函数。当然,如果您始终使用“使用块”,则这不是问题。

注意:我ktrauberman,Reed和Pontus已经就如何绕过我在下面提出的观点提出了一些好的观点。这就是我的工作,但我不能说这是做事的唯一方法。实际上,Microsoft甚至建议在某些情况下从Finalizer中调用Dispose()。但是,我将在这里留下讨论,只是为了说明为什么遵循Reed的建议很重要:在混合Destructors和Dispose()时要小心。

我不同意Reed的答案是在断言你应该通过在Finalizer中调用Dispose()来实现IDisposable类。这只会混淆两种不同的结构,很可能会导致问题。例如,如果您在使用块中创建了IDisposable类实例,则在析构函数中调用Dispose(),它将被调用两次 - 可能是令人讨厌且难以调试的崩溃(再次 - 您不控制GC的时间)。 (旁注:一般情况下,这对于Destructors来说确实如此 - 在某些情况下,它们可以不止一次被调用!)

答案 3 :(得分:2)

当对象被垃圾收集时,不会调用它。如果您想要这种行为,可以使用析构函数(终结器)和来自那里的调用Dispose()

如你所说,它会被自动调用并结束using块。

答案 4 :(得分:0)

请参阅上一个问题:Proper use of the IDisposable interface