Ostensibly, this approach makes sense when you cannot make the disposal thread-safe (for example when talking to COM objects, which MUST be released on the same thread). However, in practice, you would find that this approach leads to objects living longer than they should.
I would strive to make the disposal thread safe, that way you can call Dispose
from the finalizer and achieve truly automatic lifetime management. You have to be careful as might not be appropriate for some types of resources - like file or network handles, which might need tighter control. Otherwise, this is the best solution to this problem.
If the disposal HAS to be on the same thread then you are in a bit of a pickle. If the object needing disposing is in the model layer (as in business rules object) - it's quite likely it has layers of UI on top of, needing complex logic to dispose it (like events after a window is closed). It's a lose/lose situation and a choice between objects living forever (like your original solution) and complex disposal logic (which turns ugly pretty quickly).
Perhaps something to experiment with. You could separate the disposable resource into its own class and have the factory maintain a strong reference to it. The resource object maintains a weak reference back to business object. When the business object is finalized, WeakReference
will return null
, thus enabling you to make pass over the disposables and dump the ones that are no longer needed.
public class Global {
private static readonly List<Resource> Disposables = new List<Resource>();
public HeavyLifter GetHeavyLifter()
{
var resource = new HeavyLifterResource();
var heavyLifter = new HeavyLifter(resource);
resource.BusinessObject = heavyLifter;
Disposables.Add(resource);
}
public void DisposeAll()
{
Disposables.ForEach(d => d.CleanUp());
}
}
public abstract class Resource : IDisposable {
WeakReference<object> m_BusinessObject;
public WeakReference<object> BusinessObject {get;set;}
public CleanUp() {
if (!m_BusinessObject.IsAlive)
Dispose();
}
}
public HeavyLifter {
public HeavyLifter (Disposable d) {
m_resourceObj = d;
}
HeavyLifterResource m_resourceObj;
}
public class HeavyLifterResource :Resource {
public void Dispose() {
//disposal
}
}
Let me again reiterate that the above approach is only appropriate for a certain class of objects. You wouldn't want to handle your network connections in such fuzzy way, but you can, for example, do this for business objects that need a network connection to dispose themselves. Even then, this is only appropriate when such connection is persistent through the lifetime of the application.