Реализация IDisposable в подклассе, когда родитель также реализует IDisposable
У меня есть родительский и дочерний классы, которым необходимо реализовать IDisposable
. Где должны срабатывать вызовы virtual
(и base.Dispose()
?)? Когда я просто переопределяю вызов Dispose(bool disposing)
, мне действительно кажется странным утверждение, что я реализую IDisposable
, не имея явной функции Dispose()
(просто использую унаследованную), но имея все остальное.
То, что я делал (немного мелочи):
internal class FooBase : IDisposable
{
Socket baseSocket;
private void SendNormalShutdown() { }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
SendNormalShutdown();
}
baseSocket.Close();
}
}
~FooBase()
{
Dispose(false);
}
}
internal class Foo : FooBase, IDisposable
{
Socket extraSocket;
private bool _disposed = false;
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
extraSocket.Close();
}
base.Dispose(disposing);
}
~Foo()
{
Dispose(false);
}
}
Ответы
Ответ 1
Когда я просто переопределяю вызов Dispose (bool disposing), действительно странно заявлять, что я реализую IDisposable, не имея явной функции Dispose() (только используя унаследованную), но имея все остальное.
Это то, о чем вам не следует беспокоиться.
Когда вы подклассифицируете класс IDisposable, все сантехника "Dispose pattern" уже обрабатывается базовым классом. Вы действительно должны ничего не делать, кроме переопределения метода protected Dispose(bool)
и отслеживать, были ли вы уже настроены (правильно поднять ObjectDisposedException
.)
Подробнее см. в сообщении моего блога Подкласс из класса IDisposable.
Кроме того, часто рекомендуется рассмотреть вопрос о инкапсуляции класса IDisposable вместо его подклассификации. Бывают случаи, когда подкласс класса IDisposable подходит, но они несколько редки. Инкапсуляция часто является лучшей альтернативой.
Ответ 2
Идея этого шаблона заключается в том, что вы переопределяете виртуальный метод Dispose
, вызывая при необходимости base.Dispose
. Базовый класс заботится об остальном, вызывая виртуальный метод Dispose (и, следовательно, правильную реализацию). Подкласс не должен также реализовывать IDisposable
(это IDisposable
через наследование)
Ответ 3
Зачем усложнять вещи, когда вам это не нужно?
Поскольку вы не инкапсулируете какие-либо неуправляемые ресурсы, вам не нужно все это делать с завершением. И ваши классы являются внутренними, что предполагает, что вы контролируете иерархию наследования в своей собственной сборке.
Таким образом, подход, основанный на смещении, будет следующим:
internal class FooBase : IDisposable
{
Socket baseSocket;
private void SendNormalShutdown()
{
// ...
}
private bool _disposed = false;
public virtual void Dispose()
{
if (!_disposed)
{
SendNormalShutdown();
baseSocket.Close();
_disposed = true;
}
}
}
internal class Foo : FooBase
{
Socket extraSocket;
private bool _disposed = false;
public override void Dispose()
{
if (!_disposed)
{
extraSocket.Close();
_disposed = true;
}
base.Dispose();
}
}
Даже если у вас есть неуправляемые ресурсы, я бы сказал, что вам гораздо лучше инкапсулировать их в свой собственный одноразовый класс и использовать они, как и вы, использовали бы любое другое одноразовое; как straighforward как код выше.
Ответ 4
Детский класс должен переопределить виртуальный Dispose, выполнить любое удаление, относящееся к подклассу, и вызвать суперкласс "Dispose", который, в свою очередь, будет выполнять свою собственную работу.
EDIT: http://davybrion.com/blog/2008/06/disposing-of-the-idisposable-implementation/ - это шаблон, который я придерживаюсь в таких случаях. Не для класса "Одноразовые", а для наследования и переопределения.
Ответ 5
Я всегда обращаюсь к Джо Даффи с очень глубоким изучением этой картины. Для меня его версия - это Евангелие.
http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/
Первое, что нужно помнить, - это то, что финализатор не требуется большую часть времени. Он предназначен для очистки неуправляемых ресурсов, где вы непосредственно используете собственные ресурсы, т.е. Только ресурсы, у которых нет собственного финализатора.
Здесь приведен пример пары подкласса базового класса.
// Base class
#region IDisposable Members
private bool _isDisposed;
public void Dispose()
{
this.Dispose(true);
// GC.SuppressFinalize(this); // Call after Dispose; only use if there is a finalizer.
}
protected virtual void Dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
{
// Clear down managed resources.
if (this.Database != null)
this.Database.Dispose();
}
_isDisposed = true;
}
}
#endregion
// Subclass
#region IDisposable Members
private bool _isDisposed;
protected override void Dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
{
// Clear down managed resources.
if (this.Resource != null)
this.Resource.Dispose();
}
_isDisposed = true;
}
base.Dispose(isDisposing);
}
#endregion
Обратите внимание, что подкласс имеет свой собственный _isDisposed элемент. Также обратите внимание на проверку нулевого уровня ресурсов, поскольку вам не нужны исключения в этих блоках.
Лука