Вызов виртуальных методов из финализатора / Dispose
небезопасен, по тем же причинам небезопасно делать в конструкторе . Невозможно быть уверенным, что производный класс еще не очистил состояние, которое требуется для правильной работы виртуального метода.
Некоторые люди смущены стандартным шаблоном 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();
}
}
Вот что происходит, когда вы удаляете и объект типа 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.Whatever()
для уже удаленного D.X
-
Теперь, большинство людей, которые запускают этот код, делают одну вещь, чтобы исправить это - они перемещают вызов base.Dispose(dispose)
до того, как очистят свой собственный объект. И да, это работает. Но действительно ли вы доверяете Программисту X, Ультра-младшему разработчику из компании, для которой вы разработали C
, которому назначено писать D
, чтобы написать ее так, чтобы ошибка была обнаружена или имела вызов base.Dispose(disposing)
в нужном месте?
Я не говорю, что вы никогда не должны когда-либо писать код, который вызывает виртуальный метод из Dispose, просто вам нужно документ , что требование этого виртуального метода что он никогда не использует состояние, определенное в любом классе, производном от C
.