C# IDisposable Interface Implementation
Long long time ago I dissected the IDisposable interface (simple enough as it is) and made a "correct" implementation example essentially by commenting out MSDN code for better understanding what for and why you should do as it says:
class DisposableExample: IDisposable {
#region Absolute minimum of the code required for correct implementation of IDisposable
///
/// Field that will be set to true if attempt to destroy the object was already made.
///
private bool isDisposed = false;
///
/// This method implements IDisposable.
/// It is either called by the programmer explicitly or from using() block
/// Microsoft says that this method should not be made virtual, but does not elaborate why.
/// Most likely this is so the order of calls to Dispose()/GC.SuppressFinalize() is not messed up.
///
public void Dispose() {
//Calling the method's overload specifying that it was called explicitly:
Dispose(true);
//Cancelling destructor's call for this object — we already cleaned everything up.
//This is not critical (because we have protection from double-calling of Dispose(bool) method — isDisposed flag),
//but would help us to optimize performance.
GC.SuppressFinalize(this);
}
///
/// This is destructor otherwise known as finalizer.
/// It's being called by the Garbage Collector when the framework decides that it's time to clean up.
///
~DisposableExample() {
//Calling cleanup method specifying that it's called from the destructor.
//If we have no unmanaged reseources, "supposedly" nothing new would happen
//and there's not much sense in this call, but it's better to be safe than sorry.
//We'll come here only if the developer forgets to destroy the object and did not call Dispose()
Dispose(false);
//Nothing else should be in the destructor — all resource cleanup code shoud more to Dispose(bool)
}
///
/// Resource cleanup.
/// This method is not virtual to keep the logic in the inherited classes,
/// instead two methods DisposeManaged() and DisposeUnmanaged(), that are being called inside, are made virtual.
///
///
/// True, if Dispose() method being called explicitly or from using() block
/// False, when Dispose() method being called from destructor (during garbage collection). Do not use managed resources, the Garbage Collector will take care of them
/// In the MSDN example this parameter called "disposing" that does not really describes its purpose.
///
protected void Dispose(bool isCalledExplicitly) {
//Checking that this method has not been already called:
if(!isDisposed) {
try {
if(isCalledExplicitly) {
//Dispose() was explicitly called by the programmer,
//who wanted to free up memory, because this object will no longer be needed.
//Garbage Collector hasn't yet dealed with our object,
//therefore we release managed resources ourselves.
DisposeManaged();
}
//Releasing unmanaged resources, Garbage Collector will have no way of knowing about them,
//that's why nobody but us will clean them up
DisposeUnmanaged();
} finally {
//To avoid repeated calls to this method:
isDisposed = true;
}
}
}
///
/// Releasing internal managed objects.
/// May be overriden in derived classes with a call to base.DisposeManaged()
/// If we are writing base class without any functionality here,
/// this method should be declared abstract and not protected.
///
protected virtual void DisposeManaged() {
//We need to destroy all managed resources here
//All internal properties implementing IDisposable will be processed in this method:
//this.DisposableProperty.Dispose();
}
///
/// Freeing up internal unmanaged resources
/// May be overriden in derived classes with a call to base.DisposeUnmanaged()
/// If we are writing base class without any functionality here,
/// this method should be declared abstract and not protected.
///
protected virtual void DisposeUnmanaged() {
//Here we clean up resources unknown to .Net Framework,
//for instance, open file handles:
//CloseHandle(handle);
}
#endregion
#region Additional methods and properties that could be useful
///
/// Returns true if the object is already destroyed.
/// Tells us that this object should no longer be used.
///
protected bool IsDisposed { get { return isDisposed; } }
///
/// This method will raise exception if Dispose() was already called.
/// CheckDisposed() may be inserted at the start of other methods,
/// who care about object's integrity.
///
protected void CheckDisposed() {
if(IsDisposed) throw new ObjectDisposedException(null);
}
#endregion
}
