IDisposable series
- IDisposable and using in C#
- Implementing IDisposable
- Disposable Base Class
- Using and Disposing of WCF Clients
In the IDisposable and using in C# post I showed how to handle an object that implements IDisposable
. That’s the most common scenario, where a simple using
will ensure that resources are properly and early released, but that only handles the case when the resource is created and disposed of in the same function. What if the resource is a member of a class?
I have created a simple logging class that writes an opening line whenever the log is opened.
public class LogWriter { private StreamWriter m_Stream; public LogWriter(string logFile) { m_Stream = new StreamWriter(logFile, true); m_Stream.WriteLine("Starting logging at {0}", DateTime.Now); } public void WriteLine(string message) { m_Stream.WriteLine(message); } } |
The StreamWriter
created in the constructor implements IDisposable
so we should call it’s Dispose
method. But from where? The answer is to let the LogWriter
implement IDisposable
IDisposable implementation
The requirements on a correct IDisposable
implementation are:
- Multiple calls to
Dispose
must be handled gracefully. - Child classes must have a chance to dispose their resources, including any unmanaged resources.
- The finalizer should not be called if the object is already disposed.
To handle all these requirements there is a common pattern, which is implemented in the below example.
public class LogWriter : IDisposable { private StreamWriter m_Stream; public LogWriter(string logFile) { m_Stream = new StreamWriter(logFile, true); m_Stream.WriteLine("Starting logging at {0}", DateTime.Now); } public void WriteLine(string message) { m_Stream.WriteLine(message); } #region IDisposable implementation public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private bool m_Disposed = false; protected virtual void Dispose(bool disposing) { if (!m_Disposed) { if (disposing) { m_Stream.Dispose(); } // Unmanaged resources are released here. m_Disposed = true; } } ~LogWriter() { Dispose(false); } #endregion } |
The Dispose()
method defined by the IDisposable
interface is a wrapper, around a protected worker method Dispose(bool)
that does all the work. In the Dispose()
method there is also a call to GC.SupressFinalize(this)
. This will enable the garbage collector to release the memory of the object directly when it is no longer in use. If we omit this call, the garbage collector will have to put the object on the finalizer que instead of releasing it. Once the finalizer has been run (by a background thread) the object is eligible for garbage collection, but it won’t be collected until the GC is run again. When the finalizer is suppressed, the GC is able to collect the object on the first run.
The worker method is also called from the finalizer ~LogWriter()
to ensure that any unmanaged resources are correctly released even if Dispose()
was not called.
The main work is done in the Dispose(bool)
method. It will first check a flag to ensure that we are not disposing twice. The disposing
flag is important, because if called from dispose we want to release any managed resources early. If called from the finalizer it would be an error to touch any managed references. We have no idea if they are still alive, or if the GC freed those objects before freeing this object. Any unmanaged resources should of course be released anyways.
Inherited classes
What if we inherit the LogWriter
class? Thanks to all the plumbing code in the base class we only have to override Dispose(bool)
.
public class DetailedLogWriter : LogWriter { public DetailedLogWriter(string logFile) : base(logFile) { WriteLine(string.Format("Logging from {0}", Assembly.GetExecutingAssembly().FullName)); } private bool m_Disposed = false; protected override void Dispose(bool disposing) { if (!m_Disposed) { WriteLine(string.Format("Logging ended on {0}", DateTime.Now)); WriteLine(string.Empty); base.Dispose(disposing); } base.Dispose(disposing); } } |
More Reading
There are a couple of great articles on Code Project about IDisposable
. IDisposable: What Your Mother Never Told You About Resource Deallocation describes in depth on why the IDisposable
construct is needed. Implementing IDisposable and the Dispose Pattern Properly goes deeper into how to implement IDisposable
correctly.