What is IDisposable and the Dispose pattern?

C# Memory Intermediate 2–3 Yrs3–5 Yrs Conceptual 2 min read

Quick Answer

IDisposable defines Dispose(), a place to release resources the garbage collector doesn't manage — file handles, DB connections, sockets, native memory. You call it deterministically with using. The full Dispose pattern adds a finalizer as a safety net for the rare case where you hold a raw OS handle directly.

IDisposable is how a class says “I hold something the garbage collector won't clean up — call Dispose when you're done.” That's file handles, database connections, sockets. I call it with a using statement so it runs deterministically at the end of the scope. If my class just wraps other disposables I only need to dispose those. The full pattern with a finalizer is for when I hold a raw OS handle directly — Dispose calls GC.SuppressFinalize so the finalizer is skipped if I disposed properly — but honestly I'd use SafeHandle and never write a finalizer myself.

The GC frees managed memory but has no idea about a Win32 handle. If you don't release it, it leaks until the process exits (or a finalizer eventually runs, non-deterministically).

Simple case — you only wrap other IDisposable objects: implement Dispose() and dispose your fields. No finalizer needed.

Full pattern — you hold a raw handle:

  • Dispose() → calls Dispose(true) then GC.SuppressFinalize(this).
  • protected virtual void Dispose(bool disposing) → if disposing, release managed resources too; always release unmanaged ones.
  • A finalizer ~MyClass() → calls Dispose(false) as a backstop.

In practice, prefer SafeHandle (it has the finalizer built in) so you never write one. using var x = ...; and await using (for IAsyncDisposable) call Dispose at the end of scope.

C# — simple IDisposable + using
public sealed class ReportWriter : IDisposable
{
    private readonly StreamWriter _writer;
    public ReportWriter(string path) => _writer = new StreamWriter(path);

    public void Write(string line) => _writer.WriteLine(line);
    public void Dispose() => _writer.Dispose();
}

using var writer = new ReportWriter("out.txt");
writer.Write("done");   // Dispose runs at end of scope