Reputation: 29061
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
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
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
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
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