Dispose Pattern
When a class controls the lifetime of unmanaged resources you should at a minimum implement IDisposable
. This allows callers to deterministically release the unmanaged resource by either calling Dispose
or with a using
statement:
using (var unmanaged = GetUnmanagedThing())
{
// Doing stuff with unmanaged
}
Implementing IDisposable
is relatively straightforward. In your case each instance should be constructed with a reference to the manager and should call the manager when they are disposed. Make sure to follow these guidelines:
Dispose
can be called multiple times for a single object, make sure your Dispose
method handles this.
- Don't throw exceptions from
Dispose
unless the entire process is on fire.
- Make sure to stop methods on the object from being used when it has been disposed, normally by throwing
ObjectDisposedException
.
The MSDN Dispose Pattern Article goes into a lot more detail. That page advises you to implement dispose logic in a virtual Dispose(bool disposing)
method as this allows finalizers and derived classes to correctly handle object disposal. If your object does not have any inheritors and does not use finalization then this pattern is unnecessary and you can just implement the Dispose
method directly.
Finalization
Ensuring that unmanaged resources are destroyed when an object is garbage collected (without having been disposed first) involves finalizers, which are notoriously difficult to implement correctly because everything you know is wrong. In particular, objects can become available for collection sooner than you think, for example an object can become eligible for collection during execution of a method on that very object. Care must therefore be taken to ensure managed resources are not disposed while they are still in use.
Finalizers only run after an object is garbage collected, which is non-deterministic. There may be a lengthy delay between when an object is no longer referenced and when it is finalized which means a lengthy delay before your unmanaged resources are destroyed. In addition finalizers result in a performance penalties as the finalizer must be scheduled to run and the memory for the object cannot be reclaimed until the finalizer has finished running.
If your application is performance sensitive then you should aim to always dispose unmanaged resources using the Dispose pattern, which makes finalization unnecessary. Therefore my advice would be to avoid relying on finalization and instead ensure all parts of your application correctly use the dispose pattern. You can add logging to your finalizer to ensure warn when the dispose pattern is not correctly followed:
public void Dispose() {
// Implement dispose here....
GC.SuppressFinalize(this);
}
~UnmanagedThing() {
Log.Warning("Unmanaged thing not properly disposed");
}
If this solution isn't suitable then you may be able to use a SafeHandle to dispose of unmanaged resources.
No really, I want to write a finalizer
The general idea is to ensure that your object is unreachable, e.g. by having your manager hold only a weak reference to your object, or by having your manager hold a reference to a nested object. If your object becomes eligible for destruction then the finalizer will (hopefully) be called and can be used to release your unmanaged resources.