IDisposable and the Dispose pattern
Purpose
The GC handles managed memory. But applications often use unmanaged resources such as file handles, database connections, network sockets, GDI handles, or pointers to unmanaged memory, and the GC does not know how to release them. The IDisposable interface gives objects a contract for offering a deterministic way to release such resources. It is also crucial for managing the lifetime of managed objects that themselves wrap disposable resources, so that cleanup cascades.
The interface
IDisposable is defined in the System namespace and has a single public method: void Dispose();.
The using statement
The C# using statement is a convenient syntax that ensures Dispose() is called on an IDisposable object when it goes out of scope, even if an exception occurs inside the using block. This is the recommended way to handle disposable objects.
This is a conceptual example. Both using lines are on lines 1 and 2, and the comment on line 5 says what happens at the closing brace.
using (var fileStream = new FileStream("path/to/file.txt", FileMode.Open))using (var streamReader = new StreamReader(fileStream)){ // Use streamReader to read from the file} // streamReader.Dispose() and fileStream.Dispose() are called automatically here.Implementing the Dispose pattern
For a class that directly owns unmanaged resources or managed disposable resources, a robust implementation of the Dispose pattern typically involves:
- Implementing
IDisposable. - A public
Dispose()method. It should call a virtualDispose(bool disposing)method withdisposingset totrue, and then callGC.SuppressFinalize(this). That stops the GC from calling the object’s finalizer (if one exists), since the cleanup has already been done. - A protected virtual
Dispose(bool disposing)method that contains the actual cleanup logic. It should check whether the resources have already been disposed, to make it idempotent.
Inside Dispose(bool disposing):
- If
disposingistrue, the call came fromDispose()and not from the finalizer. Release both the managed resources (by callingDispose()on them) and the unmanaged resources. - If
disposingisfalse, the call came from the finalizerFinalizersA fail-safe for when Dispose was never called, and why you should rarely write one.Opens in a new tab. Release only the unmanaged resources. Don’t touch managed resources here, because their state might be indeterminate during finalization.
Idempotency
Dispose() and Dispose(bool) should be safe to call multiple times without throwing exceptions. Later calls should do nothing.
Sources
- Advanced Memory Management Techniques in C#, TechOnDiapers · accessed May 16, 2025
- Implement a Dispose method, .NET, Microsoft Learn · accessed May 16, 2025