【问题标题】:How to correctly implement disposable pattern with third-party libraries如何使用第三方库正确实现一次性模式
【发布时间】:2016-06-29 12:14:00
【问题描述】:

假设我有以下类:

class Foo : IDisposable
{
  public void Dispose()
  {
    bar.Dispose();
  }

  private Bar bar = new Bar();
}

class Bar : IDisposable
{
  public void Dispose()
  {
    baz.Dispose();
  }

  private SomeThirdPartyLibraryClass baz = new SomeThirdPartyLibraryClass();
}

这段代码在使用using 语句时效果很好:

using (Foo foo = new Foo())
{
  // ...
}
// All resources should be disposed at this time

但是,如果由于某种原因该类的用户忘记使用using 语句,则永远不会释放资源。

根据 MSDN,我应该通过以下方式实现一次性模式:

class Foo : IDisposable
{
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposed)
      return; 

    if (disposing) {
      // Free any managed objects here.
      //
    }

    // Free any unmanaged objects here.
    //
    disposed = true;
  }

  ~Foo()
  {
    Dispose(false);
  }

  bool disposed = false;
  private Bar bar = new Bar();
}

Bar 也是如此)

但是我应该将bar.Dispose();baz.Dispose(); 代码放在哪里?

应该在“托管清理”部分下还是在“非托管清理”部分下?

顾名思义,我不知道SomeThirdPartyLibraryClass的实现(而且反正可以随时间改变)。

那我该怎么办?

【问题讨论】:

  • MSDN 上的哪个地方建议使用终结器作为通用 IDisposable 实现的一部分?
  • Is it possible to force the use of "using" for disposable classes? 可能重复,因为您无法修改第三方库。它还包含一些包装解决方案。
  • 所有 .NET 对象都是托管的。它属于“在此处释放任何托管对象”。仅当您亲自合并非托管资源时(没有人有任何理由这样做,因为您可能想要使用的所有内容都已经包装在适合您的类中),任何事情都属于“免费的非托管资源”。第三方代码将处理 在其 自己的 Dispose(false) 路径中分配的非托管资源。
  • @FrozenHeart 因为那篇文章建议实施终结器是可取的如果您有非托管资源,这不是常见的用例。有关更多信息,请参阅Dispose, Finalization, and Resource Management

标签: c# dispose unmanaged managed


【解决方案1】:

您应该在 Dispose 的托管部分下调用 bar 和 baz 的 Dispose,因为对于您的类来说,它们是托管对象。如果 bar 和 baz 有一些不受管理的东西,那么它们应该在它们的 Dispose/Finalize 实现中由它们各自的类来清理。

【讨论】:

  • 所以在我的情况下,我可以跳过Dispose(bool) 方法和终结器,只处理我的数据成员并直接在Dispose() 方法中调用GC.SuppressFinalize(this);
  • 是的,这是有道理的,但是由于您现在没有实现 Finalize,因此不需要 SupressFinalize。
  • @DeepakBhatia:如果一个持有对具有终结器的对象的引用的类不包括访问thisDispose 方法的结尾,这种方式无法重新排序,那么这些终结器可以在某些情况在 Dispose 函数完成之前开始运行。调用 GC.KeepAlive(this)GC.SuppressFinalize(this) 是一种廉价的方法,可确保在处置完成之前,thisthis 拥有引用的任何内容都没有资格被收集。
【解决方案2】:

但是我到底应该在哪里放置 bar.Dispose();和 baz.Dispose();代码?

由于您的第三方库似乎是托管代码,因此它将进入“托管清理”部分。

【讨论】:

  • 所以在我的情况下,我可以跳过Dispose(bool) 方法和终结器,只处理我的数据成员并直接在Dispose() 方法中调用GC.SuppressFinalize(this);
  • 如果你使用简单的方法,我不知道你为什么会打电话给GC.SuppressFinalize(this);。简单的方法虽然很好。只需在您自己的 Dispose 实现中添加对托管一次性资源的调用即可。
  • @nvoigt:调用GC.SuppressFinalize(this)GC.KeepAlive(this) 将防止一些罕见但难以诊断的问题,如果该类通过确保终结器不能包含对具有终结器的对象的任何引用运行直到处置完成。
猜你喜欢
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 2023-01-22
  • 2010-10-12
  • 1970-01-01
  • 2019-12-14
  • 2011-07-22
相关资源
最近更新 更多