IDisposable and using

C# · Practical · 8 min

Leaked connections and file handles cause outages that the garbage collector will not fix in time.

The garbage collector reclaims memory, but not promptly, and it knows nothing about file handles, sockets or database connections. Types that hold such resources implement IDisposable with a Dispose() method that releases them right away.

The using statement guarantees Dispose is called, even if an exception is thrown:

using (var reader = new StreamReader(path)) { ... }

The more common using declaration disposes at the end of the enclosing scope: using var conn = new SqlConnection(cs);. Multiple declarations are disposed in reverse order.

For async resources there is IAsyncDisposable with DisposeAsync(), consumed with await using.

Guidelines:

  • If you create a disposable object and own it, dispose it. If it was given to you (for example by dependency injection), the owner disposes it.
  • Some types are meant to be long-lived: HttpClient should be reused (or created via IHttpClientFactory), not created and disposed per request.
  • Implementing IDisposable in your own class is needed when it owns other disposables: call their Dispose from yours. The full dispose pattern with a finalizer is only needed when directly wrapping unmanaged handles, which SafeHandle usually covers.
  • After disposal, objects typically throw ObjectDisposedException when used.

Example

static void Work()
{
    using var a = new Resource("A");
    using var b = new Resource("B");
    a.Use();
    b.Use();
}   // disposed in reverse order here

Work();
// using A / using B / disposed B / disposed A

try
{
    using var c = new Resource("C");
    throw new InvalidOperationException();
}
catch (InvalidOperationException) { Console.WriteLine("caught"); }
// disposed C / caught

class Resource(string name) : IDisposable
{
    public void Use() => Console.WriteLine($"using {name}");
    public void Dispose() => Console.WriteLine($"disposed {name}");
}

Remember

  • IDisposable releases non-memory resources deterministically
  • using var disposes at the end of the scope, even on exceptions
  • await using for IAsyncDisposable
  • Dispose what you own; reuse HttpClient instead of disposing it per call

Watch out for

  • Creating a new HttpClient per request, exhausting sockets under load.

Check yourself

  1. When is a using var x = ... declaration disposed?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using