IDisposable is a deceptively simple interface, but with some rules around how and when to use it you really ought to follow. Some recent work I did around it showed the extent to which myself and others have misunderstood how to properly dispose of items and what needs disposing. So this is my attempt at putting some of the good practices to “paper” so if nothing else, I’ll remember them in future and hopefully a few others can find this helpful.
So when should you implement IDisposable? When a class either has unmanaged resources, or owns managed types which are IDisposable.
Why should you implement IDisposable? It can also be used to ensure cleanup of managed resources that could cause a leak – for example, to enforce unsubscribing from an event in the case of there not being a natural place to otherwise do so (thought it’s probably better to find a good place in the code execution to unsubscribe than to make the object IDisposable for this only).
This how-to attempts to suggest a good way to implement IDisposable and provide some context around common mistakes. There are a couple of resources on IDisposable that may be useful for a more general picture:
- http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/Joe Duffy on IDisposable
- http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About – CodeProject
- https://msdn.microsoft.com/en-us/library/ms244737.aspx – FXCop rule 1063: Implement IDisposable correctly
Note that these don’t reflect how we have implemented IDisposable in some cases and CA1063 should be suppressed in current Gen. I will add more on this below.
Simplified Definitions
- Managed Resources – .Net CLR types and types created using .NET, except for types that contain unmanaged resources
- Unmanaged Resources – Pointers, handles, HWind’s and other COM types.
- Garbage collection – The process of cleaning up of managed resources that are no longer in use. Hereafter known as GC.
- Finalization – A process, related to Garbage collection, of cleaning up unmanaged resources. This runs after Dispose, in garbage collection, or on the shutdown of the application domain. (https://msdn.microsoft.com/en-us/library/system.object.finalize%28v=vs.110%29.aspx)
The common Implementation
The common implementation involves 3 methods and looks something like this:
public class SomeClass : IDisposable { // some fields that require cleanup private bool disposed = false; // to detect redundant calls // Constructor public SomeClass() { // allocate resources } // Finalizer ~SomeClass() { Dispose(false); } // Dispose method and only public part of IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // Dispose method that does the actual work. Should be overridden when needed in sub-classes. protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { // dispose-only, i.e. non-finalizable logic // Managed resources } // shared cleanup logic that can run from a finalizer // Unmanaged or native resources // Set the disposed flag disposed = true; } } }
So what does each part do?
Disposed flag:
- A local private bool used to reduce time spent and errors that may occur by running dispose logic on an object that has already been disposed.
Finalizer (Only needed if you have unmanaged resources):
- Overriding the finalizer (there in one in System.Object) tells the CLR that there are unmanaged resources to clean up relating to a given object
- Calls Dispose(false) to execute the finalizer logic
- A sort application level equivalent to the finally block of try/catch.finally – This code is executed last before an object is destroyed. This means that CLR types related to the class may have been destroyed prior to the finalizer running and so it cannot access those types.
Dispose()
- The only method in IDisposable
- Calls Dispose(true) to execute disposal of managed resources
- Runs during garbage collection, or from a direct call to Dispose, or from the close of a using block.
- If there is a Finalizer, calls GC.SupressFinalizer(this) to tell the GC that the finalizer logic has been executed and the object can be safely destroyed. This will mean the object can be destroyed 1 GC cycle earlier
Dispose(bool disposing)
- Contains the logic for disposing of an object
- Should contain an (if disposing) block for disposal of managed resources
- Code outside this block should be accessible by a finalizer. This means no calls to other types native to your project should be here. Dispose of unmanaged resources here.
- Set the disposed flag to true. The dispose method may be called several times on an object, and this is an easy performance boost.
Some General IDisposable tips
- If you implement a Finalizer, you should always implement IDisposable
- If you do not have any unmanaged resources, you should not implement a Finalizer
- If your subtypes are IDisposable, consider implementing IDisposable on the base and than just overriding Dispose(bool disposable) in your subtypes, ensuring you call base.Dispose(disposing) as the last call in the overridden method.
- Only implement IDisposable on classes when the parent has already done so – don’t re-implement Dispose() or the finalizer.
- Do not throw exceptions in the Finalizer or Dispose methods
- Do call dispose on child object that are Disposeable, that are owned by this class.
- Do call GC.SupressFinalizer(this) in your Dispose() method
- Put all Finalizer logic in the Dispose(bool disposing) method and make sure that method is structured such that when called from a finalize, it will only attempt to access object accessible by the finalize.
- Dispose() may be called multiple times, even on an already disposed object, and is supposed to handle that situation properly.
When to use a different implementation and which guidelines can we ignore?
The pattern that I have chosen in some cases does differ slightly from this. The key reason for the differentiation (and the only reason I can think of to do something different) is that the base class, nor any of it’s sub classes need to have finalizers. This means the call to Dispose(bool disposing) would always be passing in true and there is no need to call GC.SupressFinalizer(this) because we don’t have one. I didn’t want to create one just to support the pattern because the presence of one will add cycles at either garbage collection or shutdown (more likely the latter, we would have suppressed it during dispose). So here is the answer:
- When you will never use a finalizer the class, or any sub-classes that derive from it.
That said, you should keep the pattern close to the standard one, still creating the Public Dispose() method that calls another virtual method that contains the disposal logic and have sub-classes over-ride that.
A different implementation of IDisposable
protected bool Disposed { get; private set; } private bool _disposing; public void Dispose() { if (!_disposing && !Disposed) { _disposing = true; PerformDispose(); } } protected virtual void PerformDispose() { // Disposal of managed resources here Disposed = true; }
In this version, there is no finalizer. I am keeping the actual disposal logic out of the Dispose method. The sub classes override PerformDispose(). The disposing and Disposed flags handle checking to see if the object is already disposed, or is being called by recursion or in other cases where we are already in the process of disposing. PerformDispose is used as a substitute for Dispose(true) and as a guard against people attempting to add finalizes or Dispose(false) in subclasses.
Common Mistakes
These examples are all things I came across in a recent task at work
public class AClass: BaseClass, IDisposable public override void Dispose() { someObject.IsMinChanged -= new EventHandler(someObject_IsMinChanged); someObject.IsMaxChanged -= new EventHandler(someObject_IsMaxChanged); base.Dispose(); }
In this example, we are overriding the dispose method. We should move this logic protected virtual method called by Dispose on the base class instead, and not actually implement IDisposable on this class.
Managed and Unmanaged the wrong way around
private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!_disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. // WE HAVE NO UNMANAGED RESOURCES } if (_transaction != null) _transaction.Dispose(); _someObject = null; _someOtherObject = null; // Note disposing has been done. _disposed = true; } }
In this case the all caps comment that “WE HAVE NO UNMANAGED RESOURCES” is true. Therefore all resources being disposed could only be disposed by the GC and not by the finalize (there is one). Everything outside the “If (disposing) block should do a swap with the comment about unmanaged resources, except the last line setting the _disposed variable to true.
Unmanaged resources includes managed ones
private void Dispose(bool disposing) { if (disposing) DisposeManagedResources(); DisposeUnmanagedResources(); } private void DisposeManagedResources() { // Nothing yet } private void DisposeUnmanagedResources() { if (_disposed) return; var disposable = _aClass as IDisposable; if (disposable != null) disposable.Dispose(); _disposed = true; }
On the face of it, this is a really clean and well organized implementation. However they need to swap the content of DisposeUnmanagedReosurces() and DisposeManagedResources(), and move the checks on _disposed to the Dispose(bool disposing) method.
Conclusions
I Disposable is a simple and useful interface that is misunderstood. Hopefully this blog post has been helpful.