Reputation: 4416
If a singleton implements IDisposable, what is the right way of disposing and re-creating an instance? One way would be to keep _disposed flag and check for it in the Instance property, but I'm not sure it is the right way to do this. A simple example:
public sealed class Singleton: IDisposable
{
private static Singleton _instance;
private object _lock;
private UnmanagedResource _unmanaged;
private bool _disposed;
private Singleton()
{
_unmanaged = new UnmanagedResource();
_disposed = false;
_lock = new object();
}
public UnmanagedResource Unmanaged { get { return _unmanaged; } }
public static Singleton Instance
{
get
{
if (_instance == null || _disposed)
{
lock (_lock)
{
if (_instance == null || _disposed)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
public void Dispose()
{
_disposed = true;
try
{
_unmanaged.Dispose();
}
finally
{
GC.SuppressFinalize(this);
}
}
}
So that code likes this is possible (though, yes I agree, it kind of defeats the purpose of having a Singleton):
Singleton.Instance.Dispose();
Singleton.Instance.Unmanaged.UseResource(); // Unmanaged shouldn't be null.
NOTE: There is no need to overemphasize incompatibility between Singleton and IDisposable, I understand it. I need Dispose method to free unmanaged resources when ApppDomain unloads. If you have a problem with this class being called Singleton, I can rename it LoadBalancer instead. The question will still remain the same. This LoadBalancer needs to be disposable, because it's instance does not belong to anyone, but should be properly disposed.
Upvotes: 3
Views: 14674
Reputation: 48959
I am not seeing how this is going to work. Consider this hypothetical use case:
using (Singleton instance = Singleton.Instance)
{
// Do stuff with the instance.
}
How are you going to guard against multiple threads executing this code simultaneously? One thread could call Dispose
while another is still trying to use the same instance. Trying a force an API with recycling semantics into the singleton concept is like trying to fit a square peg in a round hold.
By the way, there is tangential problems worth mentioning. The Instance
property is not thread-safe. At the very least you will have to mark _instance
as volatile
. And that is only if you were using the canonical implementation of the pattern. You will never be able to make the pattern safe since you also use the _disposed
flag as extra criteria in the check. Personally, I would just scrap the double-checked locking pattern altogether.
Upvotes: 2
Reputation: 755457
Singleton and Disposable objects are largely incompatible concepts
Singletons are generally speaking alive for the lifetime of a process / AppDomain
. If you find yourself wanting to Dispose
them then you probably need to refactor your solution a bit.
If the problem is around the freeing of resources during an AppDomain
unload then put the responsibility of freeing the resource with the same object responsible for managing the life time of the AppDomain
. Then embed this resource in the Singleton
without implementing IDisposable
. This will properly separate out the concerns of the scenario.
Upvotes: 6
Reputation: 10287
if you need to replace the instance of a singleton, you should think about your design.
if you REALLY think you should do this, i'd suggest to use 2 objects ...
one singleton object that acts as a proxy, and is a real singleton (end of lifetime == end of process)
this object needs public members for everything your singleton is capable of, and one private member holding the real implementing object. all other members redirect to members of that implementation.
the second object is the disposable object that can be replaced. make sure that only your singleton object will ever hold a reference on that... this way it doesn't matter if any consumer object holds a reference on the singleton that would prevent you from replacing the object ... that ref will only point to a proxy
Upvotes: 6
Reputation: 3209
Maybe I'm asking a dumb question but why would you want to dispose a Singleton only to create a new one? Isn't the purpose to have a single instance....
This could be a design issue I'm not aware of. Maybe the "right way" in this case is to re-evaluate what you need your code to do and what pattern gets you there.
Upvotes: 0