Базовый класс IDisposable, которому принадлежит управляемый одноразовый ресурс, что делать в подклассах?

StackOverflow https://stackoverflow.com/questions/1015529

  •  06-07-2019
  •  | 
  •  

Вопрос

У меня есть базовый класс, которому принадлежит управляемый одноразовый ресурс (.NET PerformanceCounter). Я понимаю, как реализовать IDisposable в классе, чтобы я мог явно вызвать Dispose для ресурса. Из примеров, которые я видел, люди обычно используют закрытую логическую переменную-член «disposed» и установите его в true внутри Dispose. Позже, если есть попытка получить доступ к общедоступному методу или свойству, возникает исключение ObjectDisposedException, если "disposed" quot; это правда.

Как насчет подклассов? Как подклассы в своих открытых методах и свойствах узнают, что они были уничтожены? Сначала я подумал, что подклассам не нужно будет ничего особенного (например, реализовывать собственную версию Dispose), поскольку вещь, которую нужно утилизировать, находится только в базовом классе (давайте предположим, что подклассы не будут добавлять какие-либо данные, которые должен быть явно удален), и базовый класс Dispose должен обрабатывать это. Если подклассы переопределяют виртуальный метод Dispose базового класса исключительно с целью установки его собственного «disposed» переменная-член?

Вот очень урезанная версия рассматриваемой иерархии классов.

class BaseCounter : IBaseCounter, IDisposable
{
  protected System.Diagnostics.PerformanceCounter pc;
  private bool disposed;
  public BaseCounter(string name)
  {
    disposed = false;
    pc = CreatePerformanceCounter(name);
  }

  #region IBaseCounter
  public string Name
  {
    get 
    {
      if (disposed) throw new ObjectDisposedException("object has been disposed");
      return pc.CounterName;
    }
  }
  public string InstanceName
  {
    get
    {
      if (disposed) throw new ObjectDisposedException("object has been disposed");
      return pc.InstanceName;
    }
  }
  #endregion IBaseCounter

  #region IDisposable
  protected virtual void Dispose(bool disposing)
  {
    if (!disposed)
    {
      if (disposing)
      {
        if (pc != null)
        {
          pc.Dispose();
        }
        pc = null;
        disposed = true;
      }
    }
  }

  public void Dispose()
  {
    Dispose(true);
  }
  #endregion IDisposable
}

class ReadableCounter : BaseCounter, IReadableCounter //my own interface
{
  public ReadableCounter(string name)
    : base(name)
  {
  }

  #region IReadableCounter 
  public Int64 CounterValue()
  {
    return pc.RawValue;
  }
  #endregion IReadableCounter
}

class WritableCounter : BaseCounter, IWritableCounter
{
  public WritableCounter(string name)
    : base(name)
  {
  }

  #region IWritableCounter 
  public Increment()
  {
    pc.Increment();
  }
  #endregion IWritableCounter
}

В нашей системе ReadableCounter и WritableCounter являются единственными подклассами BaseCounter, и они подклассируются до еще одного уровня посредством процессов генерации кода. Дополнительный уровень подклассов добавляет только определенное имя, так что становится возможным создавать объекты, которые непосредственно соответствуют именованным счетчикам (например, если есть счетчик, который используется для подсчета количества произведенных виджетов, он в конечном итоге инкапсулируется в класс WidgetCounter . WidgetCounter содержит сведения (на самом деле, просто имя счетчика в виде строки), позволяющие создавать счетчик производительности " WidgetCounter "

.

Только разработанные кодом классы используются непосредственно разработчиками, поэтому у нас будет что-то вроде этого:

class WritableWidgetCounter : WritableCounter
{
  public WritableWidgetCounter
    : base ("WidgetCounter")
  {
  }
}

class ReadableWidgetCounter : ReadableCounter
{
   public ReadableWidgetCounter
     : base ("WidgetCounter")
   {
   }
}

Итак, вы видите, что базовый класс владеет и управляет объектом PerformanceCounter (который является одноразовым), в то время как подклассы используют PerformanceCounter.

Если у меня есть такой код:

IWritableCounter wc = new WritableWidgetCounter();
wc.Increment();
wc.Dispose();
wc.Increment();
wc = null;

Как WritableCounter мог узнать в инкременте, что он был удален? Должны ли ReadableCoutner и WritableCounter просто переопределять BaseCounter

protected virtual void Dispose(bool disposing)

что-то вроде этого:

protected virtual void Dispose(bool disposing)
{
  disposed = true; //Nothing to dispose, simply remember being disposed
  base.Dispose(disposing); //delegate to base
}

просто установить уровень ReadableCounter / WritableCounter " утилизировать " переменная-член?

Как насчет того, если базовый класс (BaseCounter) объявлен утилизированным как защищенный (или сделал его защищенным свойством)? Таким образом, подклассы могут ссылаться на него, а не добавлять метод Dispose просто для того, чтобы вспомнить, что произошло удаление.

Я скучаю по лодке на этом?

Это было полезно?

Решение

Я видел несколько одноразовых классов с открытым свойством IsDisposed. Вы можете сделать это и проверить это в своих подклассах.

Еще одна вещь, которую я сделал, - это общий защищенный метод 'Validate', который все методы подкласса вызывают (и могут переопределять). Если он возвращается, все хорошо, иначе он может бросить. Это полностью изолирует ваши подклассы от одноразовых внутренних помещений.

Другие советы

У меня есть фрагменты, которые я использую для реализации IDisposable, как в базовом классе, так и в подклассах. Вы, вероятно, хотели бы один для подкласса.

Думаю, я украл большую часть этого кода из MSDN.

Вот код для ID базового класса (не тот, который вам нужен):

#region IDisposable Members
// Track whether Dispose has been called.
private bool _disposed = false;

// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
    Dispose(true);
    // Take yourself off the Finalization queue 
    // to prevent finalization code for this object
    // from executing a second time.
    GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the 
// runtime from inside the finalizer and you should not reference 
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
    // Check to see if Dispose has already been called.
    if (!this._disposed)
    {
        // If disposing equals true, dispose all managed 
        // and unmanaged resources.
        if (disposing)
        {
            // TODO: Dispose managed resources.

        }
        // Release unmanaged resources. If disposing is false, 
        // only the following code is executed.
        // TODO: Release unmanaged resources

        // Note that this is not thread safe.
        // Another thread could start disposing the object
        // after the managed resources are disposed,
        // but before the disposed flag is set to true.
        // If thread safety is necessary, it must be
        // implemented by the client.
    }
    _disposed = true;
}

// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method 
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~Program()
{
    // Do not re-create Dispose clean-up code here.
    // Calling Dispose(false) is optimal in terms of
    // readability and maintainability.
    Dispose(false);
}
#endregion

А вот код, который я использую в подклассах (это код, который вы хотите):

#region IDisposable Members
// Track whether Dispose has been called.
private bool _disposed = false;

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.
protected override void Dispose(bool disposing)
{
    if (!this.disposed)
    {
        try
        {
            if (disposing)
            {
                // Release the managed resources you added in
                // this derived class here.
                // TODO: Dispose managed resources.
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            // TODO: Release unmanaged resources.
            _disposed = true;
        }
        finally
        {
            // Call Dispose on your base class.
            base.Dispose(disposing);
        }
    }
}
#endregion

Найдите метки TODO: .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top