“Dispose” 方法只适用于包含非托管资源的类型吗?

68

最近我和同事就实现Dispose和实现IDisposable接口的类型的价值进行了讨论。

我认为对于应该尽快清理的类型,即使没有未受控资源需要清理,实现IDisposable也是有价值的。

我的同事持不同意见。他认为如果没有未受控资源,实现IDisposable是不必要的,因为您的类型最终会被垃圾回收。

我的观点是,如果您想尽快关闭ADO.NET连接,则实现IDisposable并使用using new MyThingWithAConnection()是有意义的。 我的同事回答说,在内部,ADO.NET连接是一个未受控资源。而我回复他的回答是最终所有东西都是未受控资源

我知道有推荐的可释放模式,在这种模式下,如果调用Dispose,则释放托管和未托管资源,但只释放未受控资源如果通过终结器/destructor调用它(并且一段时间以前我在博客中分享了如何警告使用您的IDisposable类型的不当方式的消费者)。

那么,我的问题是,如果您有一个不包含未受控资源的类型,是否值得实现IDisposable


2
正如您正确指出的那样,ADO连接一种非托管资源。 - Konrad Rudolph
1
@KonradRudolph - 不是。连接被称为“托管”资源。它包含(拥有)一个非托管资源,但可能是通过SafeHandle间接拥有的。 - H H
@Henk 这就是我的意思 - 我应该更仔细地措辞,但在问题中已经以正确的方式表达了。 - Konrad Rudolph
2
我曾经需要使用 IDisposable 的唯一另一个场合,是在处理非托管资源之外,当我需要确保事件被正确取消订阅以便类可以被垃圾回收时。但这实际上是语言的缺陷:事件真的真的 非常 需要弱引用,但它们并没有。 - BlueRaja - Danny Pflughoeft
15个回答

1

通常情况下,不需要任何资源(无论是托管的还是非托管的)。IDisposable通常只是一种方便的方法来消除繁琐的try {..} finally {..},请比较:

  Cursor savedCursor = Cursor.Current;

  try {
    Cursor.Current = Cursors.WaitCursor;

    SomeLongOperation();
  }
  finally {
    Cursor.Current = savedCursor;
  }

with

  using (new WaitCursor()) {
    SomeLongOperation();
  }

这里的WaitCursor实现了IDisposable接口,以便于使用using语句:

  public sealed class WaitCursor: IDisposable {
    private Cursor m_Saved;

    public Boolean Disposed {
      get;
      private set;
    }

    public WaitCursor() {
      Cursor m_Saved = Cursor.Current;
      Cursor.Current = Cursors.WaitCursor;
    }

    public void Dispose() {
      if (!Disposed) {
        Disposed = true;
        Cursor.Current = m_Saved;
      }
    }
  }

您可以轻松地组合这些类:
  using (new WaitCursor()) {
    using (new RegisterServerLongOperation("My Long DB Operation")) {
      SomeLongRdbmsOperation();  
    }

    SomeLongOperation();
  }

1

简短回答:绝对不行。如果您的类型具有受管理或不受管理的成员,应实现IDisposable。

现在详细说明: 我已经在StackOverflow上回答了这个问题,并提供了更多关于内存管理和GC的内部细节。以下是其中的一些:

至于IDisposable实现的最佳实践,请参阅我的博客文章:

如何正确实现IDisposable模式?


1

如果对象拥有任何非托管对象或任何可处置的托管对象,则需要实现 IDisposable

如果一个对象使用非托管资源,应该实现 IDisposable。拥有可处置对象的对象应该实现 IDisposable 以确保释放底层的非托管资源。 如果遵循这个规则/约定,因此可以得出合乎逻辑的结论:不处置可处置的托管对象等同于不释放非托管资源。


1
在我的一个项目中,我有一个包含托管线程的类,我们称之为线程A和线程B,还有一个IDisposable对象,我们称之为C。
A用于在退出时处理C。 B用于使用C保存异常。
我的类必须实现IDisposable和析构函数,以确保按正确顺序处理这些内容。 是的,GC可以清理我的项目,但我的经验是,除非我管理了类的清理工作,否则会出现竞争条件。

1

如果您的类型引用了非托管资源或者持有实现IDisposable接口的对象的引用,则应该实现IDisposable接口。


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