Посмотрим на печально известный интерфейс IDisposable:
[ComVisible(true)]
public interface IDisposable
{
void Dispose();
}
и типичной реализации, как рекомендовано MSDN (я пропустил проверку, если текущий объект уже был удален):
public class Base : IDisposable
{
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// release managed
}
// release unmanaged
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Base()
{
Dispose(false);
}
}
public class Derived : Base
{
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
// release managed
}
// release unmanaged
disposed = true;
}
}
Проблема: я считаю, что эта реализация противоречит интуиции. И это также существенно отличается в базовом и производном классе. Производный класс должен предполагать, что базовый класс правильно реализован IDisposable, а затем переопределяет Dispose (bool), который даже не является частью исходного интерфейса.
Я должен признать, я придумал этот вопрос, потому что обычно я прошу младших программистов реализовать IDisposable на собеседовании. Если они точно не знают, как это должно быть сделано, они придумывают что-то близкое к этому:
public class Base : IDisposable
{
public virtual void Dispose()
{
// release managed and unmanaged
GC.SuppressFinalize(this);
}
~Base()
{
// release unmanaged
}
}
public class Derived : Base
{
public override void Dispose()
{
// release managed and unmanaged
base.Dispose();
}
~Derived()
{
// release unmanaged
}
}
Для меня эта реализация более ясна и более последовательна. Конечно, плохо то, что нам приходится выпускать неуправляемые ресурсы в двух разных местах, но важно то, что, вероятно, более 99% пользовательских классов не имеют ничего неуправляемого для размещения, поэтому в любом случае им не понадобится финализатор. Я не могу объяснить младшему программисту, почему реализация MSDN лучше, потому что я сам этого не понимаю.
Итак, мне интересно, что привело к таким необычным дизайнерским решениям (делая производный класс переопределять другой метод, чем тот, который находится в интерфейсе, и заставлять его думать о неуправляемых ресурсах, которые он, скорее всего, не содержит). Любые мысли по этому вопросу?