Подтвердить что ты не робот

Реализация IDisposable в подклассе, когда родитель также реализует IDisposable

У меня есть родительский и дочерний классы, которым необходимо реализовать IDisposable. Где должны срабатывать вызовы virtualbase.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);
    }

}
4b9b3361

Ответ 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 элемент. Также обратите внимание на проверку нулевого уровня ресурсов, поскольку вам не нужны исключения в этих блоках.

Лука