PostSharp 4.3 / / Post­Sharp Documentation / Standard Patterns / Parent/Child Relationships / Walkthrough: Automatically Disposing Children Objects
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.