从Dispose或析构函数调用虚方法是否可以?

时间:2008-09-26 03:36:24

标签: .net principles

我找不到它的引用,但我记得读过在析构函数或IDisposable的Dispose()方法中调用虚拟(多态)方法不是一个好主意。

这是真的吗?若有人可以解释原因?

4 个答案:

答案 0 :(得分:5)

从终结器/ Dispose调用虚拟方法是不安全的,原因与it is unsafe to do in a constructor相同。无法确定派生类是否还没有清除虚拟方法正确执行所需的某些状态。

有些人对标准的Disposable模式及其使用虚拟方法virtual Dispose(bool disposing)感到困惑,并且认为这样可以在处理中使用任何虚拟方法。请考虑以下代码:

class C : IDisposable {
    private IDisposable.Dispose() {
        this.Dispose(true);
    }
    protected virtual Dispose(bool disposing) {
        this.DoSomething();
    }

    protected virtual void DoSomething() {  }
}
class D : C {
    IDisposable X;

    protected override Dispose(bool disposing) {
        X.Dispose();
        base.Dispose(disposing);
    }

    protected override void DoSomething() {
        X.Whatever();
    }
}

以下是Dispose和名为D的{​​{1}}类型的对象时发生的情况:

  1. 部分代码调用d
  2. ((IDisposable)d).Dispose()调用虚拟方法C.IDisposable.Dispose()
  3. D.Dispose(bool)处置D.Dispose(bool)
  4. D.X调用D.Dispose(bool) 静态(调用的目标在编译时已知
  5. C.Dispose(bool)调用虚拟方法C.Dispose(bool)
  6. D.DoSomething()在已经处置D.DoSomething
  7. 上调用方法D.X.Whatever()
  8. 现在,大多数运行此代码的人都会做一件事来修复它 - 他们会在清除自己的对象之前将D.X调用移动。而且,是的,这确实有效。但是你真的相信程序员X,你开发base.Dispose(dispose)的公司的Ultra-Junior Developer,被指派编写C,以一种检测到错误的方式编写它,或者具有D在正确的地方打电话?

    我不是说你永远不应该永远编写从Dispose调用虚方法的代码,只需要记录那个虚方法要求它永远不会使用在base.Dispose(disposing)下面派生的任何类中定义的任何状态。

答案 1 :(得分:1)

不鼓励构造函数和析构函数中的虚方法。

原因比任何事情都更实际:虚拟方法可以通过覆盖者选择的任何方式覆盖,并且在构造期间对象初始化之类的事情,例如,必须确保以免最终结束对象具有随机空值和无效状态。

答案 2 :(得分:1)

我不相信有任何反对调用虚方法的建议。您记住的禁令可能是在终结器中引用托管对象的规则。

有一个标准模式可以定义.Net文档,了解Dispose()应该如何实现。图案设计得非常好,应该严格遵循。

要点是这样的:Dispose()是一个非虚方法,它调用虚方法Dispose(bool)。 boolean参数指示是从Dispose()(true)还是从对象的析构函数(false)调用方法。在每个继承级别,应该实现Dispose(bool)方法来处理任何清理。

当Dispose(bool)传递值false时,这表示终结器已调用dispose方法。在这种情况下,只应尝试清除非托管对象(在某些罕见情况下除外)。原因是垃圾收集器刚刚调用了finalize方法,因此必须将当前对象标记为ready-for-finalization。因此,它引用的任何对象也可能已被标记为read-for-finalization,并且由于序列是非确定性的,因此可能已经完成了最终化。

我强烈建议在.Net文档中查找Dispose()模式并准确地关注它,因为它可能会保护您免受奇怪和困难的错误!

答案 3 :(得分:0)

要扩展Jon的答案,如果您需要处理该级别的资源,则应该覆盖子类上的dispose或析构函数,而不是调用虚方法。

虽然,我不相信这里的行为有“规则”。但一般的想法是,您希望将资源清理仅隔离到该实现级别的实例。