Базовый класс IDisposable, которому принадлежит управляемый одноразовый ресурс, что делать в подклассах? - PullRequest
0 голосов
/ 19 июня 2009

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

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

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

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
}

просто установить переменную члена «disposed» уровня ReadableCounter / WritableCounter-уровня?

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

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

Ответы [ 2 ]

0 голосов
/ 20 июня 2009

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

Я считал большую часть этого кода из MSDN.

Вот код для базового класса IDisposable (не тот, который вы хотите):

#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:.

0 голосов
/ 19 июня 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...