views:

419

answers:

2

I have a base class that owns a managed disposable resource (.NET PerformanceCounter). I understand about implementing IDisposable on the class so that I can explicitly call Dispose on the resource. From the examples I have seen, people typically use a private boolean member variable "disposed" and set it to true inside of Dispose. Later, if there is an attempt to access a public method or property, an ObjectDisposedException is raised if "disposed" is true.

What about in the subclasses? How would the subclasses, in their public methods and properties, know that that they had been disposed? At first I thought that the subclasses would not have to anything special (like implement their own version of Dispose) since the thing that needs to be disposed is only in the base class (let's assume that the subclasses won't be adding any data that needs to be explicitly disposed) and the base class' Dispose should handle that. Should the subclasses override the base class' virtual Dispose method solely for the purpose of setting its own "disposed" member variable?

Here is a very stripped-down version of the class hierarchy in question.

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
}

In our system, ReadableCounter and WritableCounter are the only subclasses of BaseCounter and they are only subclassed to one more level via a code generation processes. The additional subclassing level only addes a specific name so that it becomes possible to create objects that correspond directly to named counters (e.g. if there is a counter that is used to count the number of widgets produced, it ends up being encapsulated in a WidgetCounter class. WidgetCounter contains the knowledge (really, just the counter name as a string) to allow the "WidgetCounter" performance counter to be created.

Only the code-generated classes are used directly by developers, so we would have something like this:

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

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

So, you see that the base class owns and manages the PerformanceCounter object (which is disposable) while the subclasses use the PerformanceCounter.

If I have code like this:

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

How could WritableCounter know, in Increment, that it had been disposed? Should ReadableCoutner and WritableCounter simply override the BaseCounter's

protected virtual void Dispose(bool disposing)

something like this:

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

simply to set a ReadableCounter/WritableCounter-level "disposed" member variable?

How about if the base class (BaseCounter) declared disposed as protected (or made it a protected property)? That way, the subclasses could refer to it rather than adding a Dispose method simply for the purpose of remembering that Dispose had happened.

Am I missing the boat on this?

A: 

I have seen some disposable classes with a public IsDisposed property. You could do that and check it in your sub-classes.

Another thing I've done is a generic protected 'Validate' method that all sub-class methods call (and could override). If it returns, all is well, otherwise it might throw. That would insulate your sub-classes from the disposable innards altogether.

n8wrl
I think that this answer as well as the next answer from Alan McBee are valid. For my situation, I think that this answer is closer to giving the behavior that I want (base class is disposable, subclasses are also disposable, but don't really have their own "disposable" resources). I found a similar approach here:http://gregbeech.com/blogs/tech/archive/2007/03/07/implementing-and-using-the-idisposable-interface.aspx
wageoghe
A: 

I have snippets that I use for implementing IDisposable, both in the base class and in the subclasses. You'd probably want the one for the subclass.

I swiped most of this code from MSDN, I think.

Here's the code for the base class IDisposable (not the one you want):

#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

And here's the code I use in the subclasses (this is the code you want):

#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

Look for the TODO: marks.

Alan McBee