3Dave
3Dave

Reputation: 29061

Best practices for handling IDisposable

I have a class hierarchy, each member of which may create IDisposable objects.

I added a List<IDisposable> property to the base class in this hierarchy, to which I add any disposable objects on creation. The root Dispose method iterates through this list and calls Dispose for each item in its list and clears the list. In the application, I explicitly call the top object's Dispose method, causing disposal to cascade through the hierarchy.

This works, but is there a better way? Am I unwittingly duplicating some functionality already present in the framework?

(Note - the objects in question have a lifetime that precludes just wrapping them in a using block or disposing of them in the same methodwhere they are created.)

Edit

Just for clarification - I'm only keeping those objects around that need to be kept. Some are disposed of in the same method where they are created, but many are used in such a way that this isn't possible.

Upvotes: 16

Views: 20756

Answers (5)

supercat
supercat

Reputation: 81277

If one object will create many other IDisposable objects and maintain ownership of them throughout its existence, the pattern you describe can be a good one. It may be enhanced by having your class implement method "T RegDispos<T>(T thing) where T:IDisposable;" which will add a disposable to the list and return it. One can thus take care of the creation and cleanup of a resource in the same statement, by replacing a statement like "someField = someDisposType.CreateThing();" with "someField = RegDispos(someDisposType.CreateThing());".

If your class doesn't expose a public constructor (requires outsiders use factory methods), and if you're either using vb.net or are willing to use thread-static fields, you can even combine initialization and cleanup with declaration (e.g. "var someField = RegDispos(someDisposType.CreateThing());"). For this to be safe, the constructor must be invoked within a try/catch or try/finally block that can call Dispose on created sub-objects if construction fails. Because field initializers in C# don't have access to constructor parameters (a weakness in the language, IMHO) the only way to implement such a pattern is to have a factory method create the list and put it into a thread-static variable which can then be read by a static RegDispos method.

Upvotes: 1

Gert Arnold
Gert Arnold

Reputation: 109253

Sounds like a situation where the visitor pattern could be appropriate. Although I never understand the claim that it extends your classes and leaves them unchanged, because I only know examples where classes should have an AcceptVisitor method or the like. BTW, it's not a pattern I like because it is complex and tends to clutter code.

Upvotes: 1

TheCodeKing
TheCodeKing

Reputation: 19230

No that is correct. IDisposable is designed to free up unmanaged resources and should be called as soon as possible after you have finished with the instance. It's a common misconception that this is unnecessary, or that the finailizer will do this automatically when the object is garbage collected. It does not.

The correct pattern for IDisposable is here, and included below for quick reference.

public class Resource : IDisposable 
{
    // Dispose() calls Dispose(true)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // NOTE: Leave out the finalizer altogether if this class doesn't 
    // own unmanaged resources itself, but leave the other methods
    // exactly as they are. 
    ~Resource() 
    {
        // Finalizer calls Dispose(false)
        Dispose(false);
    }

    // The bulk of the clean-up code is implemented in Dispose(bool)
    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources here if there are any
    }
}

Upvotes: 14

to StackOverflow
to StackOverflow

Reputation: 124794

If you're talking about arbitrary IDisposable objects, I don't believe it exists.

The System.ComponentModel.Container class implements cascading Dispose, but requires the elements to implement IComponent. If you control your IDisposable objects you could make them implement IComponent - it only requires implementing a single property Site that can return null.

Upvotes: 2

Oded
Oded

Reputation: 499312

So long as you implement the disposable pattern correctly (as described here), this method is fine.

As far as I know, only using statements have special support for IDisposable - there isn't anything in the framework that replicates what you are doing.

Upvotes: 12

Related Questions