Finding objects that are not disposed
Making sure objects are properly disposed could be tricky. Here are some ways I deal with this.
The problem
When you are creating an object that implements
IDisposable
,
you are supposed to dispose this object when you are done with it. This way the
object has a chance to close files, close network connections. If you do not do
this these resources will be in use for an unknown amount of time and this could
cause unexpected errors.
Normally you do this with the using
keyword:
Doing like this myMemoryStream
will be disposed when it is no longer in use.
In principle this is easy. But it is easy to simply forget about this. And there are more complicated scenarios were this is not possible.
The solution
One solution I have been using for a long time only works for classes you
implement yourself and are using IDisposable
. But it is neat anyway.
Look on this sample code:
Here MyDisposable
implements the normal dispose
pattern.
But I have done a minor tweak, I have added this destructor code:
This is the
destructor/finalizer
of MyDisposable
. When the object is properly disposed,
GC.SuppressFinalize(this)
is called and this will make sure that the finalizer
is never executed. If the object is not disposed, then the destructor is
executed. This will first check if the debugger is attached, and if it is it
will call
System.Diagnostics.Debugger.Break();
which will behave exactly like a breakpoint. Then debugger will stop and now you
know something has went wrong.
Detecting the issue during compiling
Another solution I have started to use recently is to use the code quality analyzers that are enabled by default in .NET 5. But the specific analyzer CA2000: Dispose objects before losing scope is disabled by default. To change it, do this:
- Go a project in Visual Studio.
- Select Dependencies.
- Select Analyzers.
- Select Microsoft.CodeAnalysis.NetAnalyzers.
- Right click on the rule CA2000 and then set the Severity to Warning.
- The setting is now saved in .editorconfig.
- If you do not have an .editorconfig before, this file is now created on the project level. You are then being suggested to move it to the solution level, which is probably a good choice.
An editorconfig file with this rule enabled looks like this:
It is worth mention that CA2000 only covers one scenario, in my experience the most common one. There are more analyzers that covers more scenarios with dispose:
- CA1001: Types that own disposable fields should be disposable
- CA1063: Implement IDisposable correctly
- CA1816: Call GC.SuppressFinalize correctly
- CA2000: Dispose objects before losing scope
- CA2213: Disposable fields should be disposed
- CA2215: Dispose methods should call base class dispose
- CA2216: Disposable types should declare finalizer
Be aware that analyzers will make it slower to compile your code. It is a not a major issue but something to be aware about. Also notice that the analyzers do not work on .NET Standard libraries, at least not by default.
Summary
Disposing objects properly are important so I use both solutions mentioned above.