可以从Dispose或者析构函数调用虚拟方法吗?
我找不到对此的引用,但我记得读过,在析构函数中调用虚拟(多态)方法或者IDisposable的Dispose()方法不是一个好主意。
这是真的吗?如果可以的话,有人可以解释为什么吗?
解决方案
在构造函数和析构函数中均不建议使用虚方法。
原因比任何东西都更实际:可以用覆盖程序选择的任何方式覆盖虚拟方法,例如,必须确保在构造过程中进行对象初始化之类的操作,以免最终得到带有随机空值和无效值的对象状态。
为了扩展Jon的答案,如果我们需要在该级别上处理资源,则应该在子类上覆盖dispose或者destructor,而不是调用虚拟方法。
虽然,我不认为此处的行为有"规则"。但是一般的想法是,我们希望将资源清理仅隔离到该实现级别的那个实例。
我认为不存在反对调用虚方法的任何建议。我们要记住的禁止可能是禁止在终结器中引用托管对象的规则。
有一个标准模式定义了.Net文档,以说明应如何实现Dispose()。该模式的设计非常好,应密切注意。
要点是:Dispose()是一种非虚拟方法,它调用虚拟方法Dispose(bool)。布尔参数指示是从Dispose()(true)还是从对象的析构函数(false)调用该方法。在继承的每个级别,都应实现Dispose(bool)方法来处理所有清理。
当Dispose(bool)传递值为false时,这表明终结器已调用了dispose方法。在这种情况下,仅应尝试清除非托管对象(在某些罕见情况下除外)。这样做的原因是垃圾收集器刚刚调用了finalize方法,因此当前对象必须已标记为可以完成。因此,它引用的任何对象也可能已标记为"可读以最终确定",并且由于该序列不确定,因此最终确定可能已经发生。
我强烈建议我们在.Net文档中查找Dispose()模式,并严格按照它进行操作,因为它可能会保护我们免受怪异和棘手的错误的侵害!
从finalizer /Dispose
调用虚拟方法是不安全的,出于同样的原因,在构造函数中执行虚拟方法也是不安全的。无法确定派生类尚未清除虚拟方法需要正确执行的某些状态。
一些人对标准的Disposable模式及其使用的虚拟方法" virtual Dispose(bool 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(); } }
这是当我们处理和释放类型为D的对象d时发生的情况:
- 一些代码调用
(((IDisposable)d).Dispose()
- C.IDisposable.Dispose()调用虚拟方法D.Dispose(bool)
- D.Dispose(bool)处理D.X
- D.Dispose(bool)静态地调用C.Dispose(bool)(在编译时就知道了调用的目标)
- C.Dispose(bool)调用虚拟方法D.DoSomething()
D.DoSomething
在已经处理过的D.X
上调用方法D.X.Whatever()
。- ?
现在,大多数运行此代码的人都会做一件事来对其进行修复-在清理自己的对象之前,他们将base.Dispose(dispose)调用移至。而且,是的,确实有效。但是我们真的信任程序员X,我们开发C的公司的超中级开发人员,被分配为写D的方式,以便以检测到错误或者具有错误为基础的方式来编写它。 (处置)在正确的位置打电话?
我并不是说我们永远不要编写从Dispose调用虚拟方法的代码,只是我们需要记录该虚拟方法的要求,即它绝不使用在C之下派生的任何类中定义的任何状态。