Реализация IDisposable на подклассе, когда родитель также реализует IDisposable - PullRequest
25 голосов
/ 23 марта 2010

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

}

Ответы [ 5 ]

25 голосов
/ 23 марта 2010

Когда я просто переопределяю вызов Dispose (bool dispose), мне очень странно заявлять, что я реализую IDisposable, не имея явной функции Dispose () (просто используя унаследованную), но имея все остальное.

Это то, что вас не должно волновать.

Когда вы создаете подкласс класса IDisposable, базовый класс для вас уже обрабатывает все сантехнические параметры «Уничтожить шаблон». Вы действительно должны ничего не делать, кроме как переопределить метод protected Dispose(bool) и отслеживать, были ли вы уже утилизированы (чтобы правильно поднять ObjectDisposedException.)

Подробнее см. В моем блоге Создание подклассов из класса IDisposable .


Кроме того, часто рекомендуется инкапсулировать класс IDisposable, а не создавать его подклассы. Бывают случаи, когда подклассы IDisposable класса уместны, но они встречаются довольно редко. Инкапсуляция часто является лучшей альтернативой.

5 голосов
/ 09 июня 2010

Зачем все усложнять, когда вам не нужно?

Так как вы не инкапсулируете какие-либо неуправляемые ресурсы, вам не нужно все это копаться с финализацией. И ваши классы являются внутренними, что говорит о том, что вы управляете иерархией наследования в вашей собственной сборке.

Итак, прямой подход будет:

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(); 
  } 
} 

Даже если у вас есть неуправляемые ресурсы, я бы сказал, что вам гораздо лучше инкапсулировать их в их собственный одноразовый класс и использовать их так же, как и любые другие одноразовые; так же просто, как и код выше.

3 голосов
/ 23 марта 2010

Идея этого шаблона заключается в том, что вы переопределяете виртуальный метод Dispose, вызывая base.Dispose при необходимости. Базовый класс позаботится об остальном, вызывая виртуальный метод Dispose (и, следовательно, правильную реализацию). Подкласс не должен также реализовывать IDisposable (это IDisposable через наследование)

1 голос
/ 23 марта 2010

Дочерний класс должен переопределить виртуальное удаление, выполнить любое удаление, специфичное для подкласса, и вызвать суперкласс Dispose, который, в свою очередь, выполнит свою собственную работу.

РЕДАКТИРОВАТЬ: http://davybrion.com/blog/2008/06/disposing-of-the-idisposable-implementation/ это шаблон, которому я следую в таких случаях. В частности, не класс «Одноразовые», а наследование и переопределения.

0 голосов
/ 13 января 2015

Я всегда обращаюсь к очень углубленному изучению этого паттерна Джо Даффи.Для меня его версия - Евангелие.

http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/

Первое, что нужно помнить, это то, что в большинстве случаев финализатор не нужен.Он предназначен для очистки неуправляемых ресурсов, в которых вы непосредственно храните собственные ресурсы, то есть только те ресурсы, у которых нет собственного финализатора.

Вот пример пары подклассов базового класса.* Обратите внимание, что у подкласса есть свой собственный член _isDisposed.Также обратите внимание на нулевую проверку ресурсов, поскольку вы не хотите никаких исключений в этих блоках.

Luke

...