As Dispose() and finalizers are aimed to different purposes, a class managing external memory-heavy resources should implement both of them. The consequence is writing the class so that it handles well two possible scenarios:
One solution is writing the cleanup code in such a way that running it once or twice would produce the same result as running it only once. Feasibility depends on the nature of the cleanup, for instance:
A safer solution is ensuring by design that the cleanup code is called once and only once whatever the external context. This can be achieved the "classic way" using a dedicated flag:
public class DisposableFinalizable1: IDisposable
{
private bool disposed = false;
~DisposableFinalizable1() { Cleanup(); }
public void Dispose() { Cleanup(); }
private void Cleanup()
{
if(!disposed)
{
// Actual code to release resources gets here, then
disposed = true;
}
}
}
Alternately, the Garbage Collector provides a specific method SuppressFinalize() that allows skipping the finalizer after Dispose has been invoked:
public class DisposableFinalizable2 : IDisposable
{
~DisposableFinalizable2() { Cleanup(); }
public void Dispose()
{
Cleanup();
GC.SuppressFinalize(this);
}
private void Cleanup()
{
// Actual code to release resources gets here
}
}