I've stumbled across Microsoft's recommended way to implement the IDisposable pattern many times, it's even present in Visual Studio as an "Implement Interface" option in the lamp icon menu. It looks like this:
// Override only if 'Dispose(bool disposing)' has code to free unmanaged resources
~Foo() {
// Do not change this code.
Dispose(calledByFinalizer: true);
}
public void Dispose() {
// Do not change this code.
Dispose(calledByFinalizer: false);
GC.SuppressFinalize(this);
}
// Put cleanup code here
protected virtual void Dispose(bool calledByFinalizer) {
if (_disposed) return;
if (!calledByFinalizer) { /* dispose managed objects */ }
/* free unmanaged resources and set large fields to null */
_disposed = true;
}
I refactored the suggested code a bit (because Dispose(bool disposing) can break someone's brain, and nested if's can break someone's eyes).
But I still have some questions on my mind:
- It is assumed that the method will be called once. Then why is
_disposed = trueplaced at the end of the method and not at the beginning? IfIDisposable.Dispose()is called from different threads, then they can all bypass theif (_disposed) return;check and actually execute the method body twice. Why not do it like this:
if (_disposed) return;
else _disposed = true;
- Why is
protected virtual void Dispose(bool disposing)flagged asvirtual? Any derived class does not have access to the_disposedfield and can easily break its behavior. We can only mark asvirtualthe optional part where the derived class can do anything without callingbase.Dispose():
~Foo() => FreeUnmanagedResources();
public void Dispose() {
if (_disposed) return;
else _disposed = true;
DisposeManagedObjects();
FreeUnmanagedResources();
GC.SuppressFinalize(this);
}
protected virtual void DisposeManagedObjects() { }
protected virtual void FreeUnmanagedResources() { }
