Walkthrough: Automatically Disposing Children Objects

When you are working with hierarchies of objects, you sometimes run into situations where you need to properly dispose of an object. Not only will you need to dispose of that object, but you likely will need to walk the object tree and recursively dispose children of that object. To do this, we typically implement the IDisposable pattern and manually code the steps required to shut down the desired objects, and call the Dispose() method on other children objects. This cascading of disposals takes a lot of effort and it is prone to mistakes and omissions.

The DisposableAttribute aspect relies on the AggregatableAttribute aspect and, as a result, is able to make use of the VisitChildren(ChildVisitor, ChildVisitorOptions, Object) method to cascade disposals through child objects.

This topic contains the following sections:

Disposing of object graphs

Adding Disposable to an object

  1. On the top level object add the DisposableAttribute.

    C#
    [Disposable]
    public class HomeMadeLogger 
    {
      private TextWriter _textWriter;
      private Stream _stream;
      private MessageFormatter _formatter;
    
      public HomeMadeLogger(MessageFormatter formatter)
      {
        _formatter = formatter;
        _stream = new FileStream("our.log", FileMode.Append);
        _textWriter = new StreamWriter(_stream);
      }
    
      public void Debug(string message)
      {
        _textWriter.WriteLine(_formatter.Format(message));
      }
    }

    At this point the IDisposable interface will be implemented on the HomeMadeLogger class. Now any code making use of HomeMadeLogger is able to execute the HomeMadeLogger.Dispose() method.

  2. Identify the fields and properties that represents references to child objects. To do this, add the ChildAttribute to the fields and properties that expose those child objects, and the ParentAttribute to fields and properties that represent plain references. In the case of the HomeMadeLogger there are two objects, the _stream and the _textWriter fields, which should also be disposed when the HomeMadeLogger is disposed.

    C#
    [Disposable]
    public class HomeMadeLogger 
    {
      [Child]
      private TextWriter _textWriter;
      [Child]
      private Stream _stream;
      [Reference]
      private MessageFormatter _formatter;
    
      public HomeMadeLogger(MessageFormatter formatter)
      {
        _formatter = formatter;
        _stream = new FileStream("our.log", FileMode.Append);
        _textWriter = new StreamWriter(_stream);
      }
    
      public void Debug(string message)
      {
        _textWriter.WriteLine(_formatter.Format(message));
      }
    }

    The _stream and _textWriter child objects will now have their Dispose() method called automatically when the HomeMadeLogger is disposed. Since both the _stream and _textWriter objects are framework types that already implement IDisposable, adding the DisposableAttribute aspect to those object types is not necessary.

Note Note

Fields that are marked as children but are assigned to a object that does not implement IDisposable (either manually or through DisposableAttribute) will simply be ignored during disposal.

Disposing of child collections

Child objects that need to be disposed of don’t always exist in a one-to-one relationship with the parent object. It’s common to see collections of child objects that need disposal as well. In that case you need to dispose of each entry in a collection.

Adding Disposable to collections

  1. Use a collection type that supports the IAggregatable interface. We do this by making use of AdvisableCollection<T> or AdvisableDictionary<TKey, TValue> class.

    C#
    [Disposable]
    public class HomeMadeLogger
    {
        [Child]
        public AdvisableCollection<Context> LoggingContexts { get; set; }
    }
  2. To make the LoggingContexts collection automatically dispose when the HomeMadeLogger object disposes you need to ensure that the Contexct class has the DisposableAttribute aspect on it.

    C#
    [Disposable]
    public class Context
    {
        //...
    }

With that, the HomeMadeLogging class and all of the child objects in the LoggingContexts collection are hooked together and they will all be disposed of in an orderly fashion when an instance of the HomeMadeLogging object is disposed.

Customizing the Dispose logic

There will be times when you have objects that need custom disposal logic. At the same time you may want to implement a parent child relationship and make use of the DisposableAttribute.

To add your own logic to the Dispose method:

  1. Create a method with exactly the following signature:

    C#
    protected virtual void Dispose( bool disposing )
  2. Include your own logic.

In the following example, we are customizing the Dispose pattern to expose the IsDispose property:

C#
[Disposable]
public class MessageFormatter : Formatter
{

  [Child]
  MessageSink sink;

  public bool IsDisposed { get; private set; }

  protected virtual void Dispose( bool disposing )
  {
    this.IsDisposed = true;
  }
}

Once you have done this, PostSharp will properly run your custom Dispose logic as well as running any of the parent and child implementations of the DisposableAttribute that exist for the object.

Caution note Caution

The DisposableAttribute aspect does not automatically dispose the object when it is garbage collected. That is, the aspect does not implement a destructor. If you need a destructor, you have to do it manually and invoke the Dispose.