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:
HttpClientshould be reused (or created viaIHttpClientFactory), not created and disposed per request. - Implementing
IDisposablein your own class is needed when it owns other disposables: call theirDisposefrom yours. The full dispose pattern with a finalizer is only needed when directly wrapping unmanaged handles, whichSafeHandleusually covers. - After disposal, objects typically throw
ObjectDisposedExceptionwhen 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.