It can be safe to not call Dispose
, but the problem is knowing when this is the case.
A good 95% of IEnumerator<T>
implementations have a Dispose
that's safe to ignore, but the 5% is not just 5% that'll cause a bug, but 5% that'll cause a nasty hard to trace bug. More to the point, code that gets passed an IEnumerator<T>
will see both the 95% and the 5% and won't be able to dynamically tell them apart (it's possible to implement the non-generic IEnumerable
without implementing IDisposable
, and how well that turned out can be guessed at by MS deciding to make IEnumerator<T>
inherit from IDisposable
!).
Of the rest, maybe there's 3 or 4% of the time it's safe. For now. You don't know which 3% without looking at the code, and even then the contract says you have to call it, so the developer can depend on you doing so if they release a new version where it is important.
In summary, always call Dispose()
. (I can think of an exception, but it's frankly too weird to even go into the details of, and it's still safe to call it in that case, just not vital).
On the question of implementing IDisposable
yourself, avoid the pattern in that accursed document.
I consider that pattern an anti-pattern. It is a good pattern for implementing both IDisposable.Dispose
and a finaliser in a class that holds both managed and unmanaged resources. However holding both managed IDisposable
and unmanaged resources is a bad idea in the first place.
Instead:
If you have an unmanaged resource, then don't have any unmanaged resources that implement IDisposable
. Now the Dispose(true)
and Dispose(false)
code paths are the same, so really they can become:
public class HasUnmanaged : IDisposable
{
IntPtr unmanagedGoo;
private void CleanUp()
{
if(unmanagedGoo != IntPtr.Zero)
{
SomeReleasingMethod(unmanagedGoo);
unmanagedGoo = IntPtr.Zero;
}
}
public void Dispose()
{
CleanUp();
GC.SuppressFinalize(this);
}
~HasUnmanaged()
{
CleanUp();
}
}
If you have managed resources that need to be disposed, then just do that:
public class HasUnmanaged : IDisposable
{
IDisposable managedGoo;
public void Dispose()
{
if(managedGoo != null)
managedGoo.Dispose();
}
}
There, no cryptic "disposing" bool (how can something be called Dispose
and take false
for something called disposing
?) No worrying about finalisers for the 99.99% of the time you won't need them (the second pattern is way more common than the first). All good.
Really need something that has both a managed and an unmanaged resource? No, you don't really, wrap the unmanaged resource in a class of your own that works as a handle to it, and then that handle fits the first pattern above and the main class fits the second.
Only implement the CA10634 pattern when you're forced to because you inherited from a class that did so. Thankfully, most people aren't creating new ones like that any more.