2

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

这是真的吗,如果是这样,有人可以解释为什么吗?

4

4 回答 4

5

从 finalizer/ 调用虚方法Dispose是不安全的,出于同样的原因,在构造函数中这样做是不安全的。无法确定派生类尚未清除虚方法正确执行所需的某些状态。

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

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 和 type 的对象时发生的情况D,称为d

  1. 一些代码调用((IDisposable)d).Dispose()
  2. C.IDisposable.Dispose()调用虚方法D.Dispose(bool)
  3. D.Dispose(bool)处置D.X
  4. D.Dispose(bool)C.Dispose(bool) 静态调用(调用的目标在编译时已知)
  5. C.Dispose(bool)调用虚方法D.DoSomething()
  6. D.DoSomething调用D.X.Whatever()已经释放的方法D.X
  7. ?

现在,大多数运行此代码的人都会做一件事来修复它——他们base.Dispose(dispose)在清理自己的对象之前将调用移至。而且,是的,这确实有效。但是,您真的相信 Programmer X,来自您开发的公司的 Ultra-Junior Developer C,被分配到 write D,以一种检测到错误或base.Dispose(disposing)在正确位置调用的方式编写它?

我并不是说您永远不应该编写从 Dispose 调用虚拟方法的代码,只是您需要记录该虚拟方法的要求,即它永远不会使用在下面派生的任何类中定义的任何状态C

于 2008-09-26T05:46:42.420 回答
1

在构造函数和析构函数中都不鼓励使用虚方法。

原因比任何事情都更实际:可以以覆盖器选择的任何方式覆盖虚拟方法,并且必须确保在构造过程中初始化对象等事情,以免最终得到一个具有随机空值和无效的对象状态。

于 2008-09-26T03:41:01.287 回答
1

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

.Net 文档中有一个标准模式定义了如何实现 Dispose()。该模式设计得非常好,应该密切关注。

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

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

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

于 2008-09-26T05:20:17.037 回答
0

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

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

于 2008-09-26T03:53:49.757 回答