using and IDisposable
The garbage collector frees memory automatically, but some objects hold resources it knows nothing about: open files, network connections, database handles, locks. These must be released promptly and reliably. Such objects implement IDisposable, and the using statement guarantees their Dispose method runs — even when an exception is thrown.
The problem
A file stays open, and may stay locked, until it's closed. If an exception happens between opening and closing, the close line never runs:
var writer = new StreamWriter("log.txt"); writer.WriteLine("Starting"); DoRiskyWork(); // if this throws... writer.Dispose(); // ...this never runs and the file stays open
You could wrap it in try/finally, but C# has a shorter way.
The using statement
using (…) { … } creates the object, runs the block, and calls Dispose at the end — in a hidden finally, so it always happens:
using (var writer = new StreamWriter("greeting.txt")) { writer.WriteLine("Hello"); writer.WriteLine("from a using block"); } // writer.Dispose() runs here: the file is flushed and closed Console.WriteLine(File.ReadAllText("greeting.txt"));
Hello from a using block
using declarations
The shorter using declaration drops the braces: the object is disposed when the variable goes out of scope — at the end of the enclosing block or method. This is the style you'll see most in modern code:
WriteReport("report.txt", ["alpha", "beta", "gamma"]); Console.WriteLine(File.ReadAllText("report.txt")); static void WriteReport(string path, string[] items) { using var writer = new StreamWriter(path); writer.WriteLine($"Report with {items.Length} items"); foreach (string item in items) writer.WriteLine($"- {item}"); } // disposed here
Report with 3 items - alpha - beta - gamma
Disposal order and exceptions
Several resources are disposed in reverse order of creation. And disposal still happens when an exception escapes:
try { using var a = new Resource("A"); using var b = new Resource("B"); Console.WriteLine("Working..."); throw new InvalidOperationException("Something failed"); } catch (InvalidOperationException ex) { Console.WriteLine($"Caught: {ex.Message}"); } class Resource : IDisposable { private readonly string _name; public Resource(string name) { _name = name; Console.WriteLine($"Open {_name}"); } public void Dispose() => Console.WriteLine($"Dispose {_name}"); }
Open A Open B Working... Dispose B Dispose A Caught: Something failed
Both resources were released before the catch block ran.
Implementing IDisposable
If your class owns a disposable object, it should be disposable too, and its Dispose should dispose what it owns. Make Dispose safe to call more than once, and have other methods refuse to work after disposal:
using (var log = new FileLogger("app.log")) { log.Write("App started"); log.Write("User signed in"); } Console.Write(File.ReadAllText("app.log")); var closed = new FileLogger("other.log"); closed.Dispose(); closed.Dispose(); // safe: does nothing the second time closed.Write("too late"); sealed class FileLogger(string path) : IDisposable { private readonly StreamWriter _writer = new(path); private bool _disposed; private int _lines; public void Write(string message) { ObjectDisposedException.ThrowIf(_disposed, this); _writer.WriteLine($"{++_lines:D3} {message}"); } public void Dispose() { if (_disposed) return; _writer.Dispose(); _disposed = true; } }
001 App started 002 User signed in Unhandled exception. System.ObjectDisposedException: Cannot access a disposed object. Object name: 'FileLogger'.
Classes that directly wrap unmanaged resources (raw operating-system handles) need a more elaborate pattern with a finaliser. That's rare in application code — wrap such handles in SafeHandle or use existing .NET classes, which already do it.
Common disposable types
FileStream,StreamReader,StreamWriter— files (lesson 54)HttpResponseMessage— HTTP responses (lesson 57)- Database connections and commands, such as
SqlConnection CancellationTokenSource,Timer,SemaphoreSlim- Many objects from graphics, cryptography and compression libraries
A rule of thumb: if a type implements IDisposable and you created it, put it in a using. (A notable exception is HttpClient, which is designed to be created once and reused for the life of the application.)
using with any disposable
using works with any IDisposable, not just resources, which makes it handy for "do something now, undo it later" helpers. Here Indent() increases the indentation of log messages and the returned object restores it when disposed — even if an exception is thrown in between:
Log.Write("Deploying"); using (Log.Indent()) { Log.Write("Building"); using (Log.Indent()) { Log.Write("Compiling 12 files"); } Log.Write("Uploading"); } Log.Write("Done"); static class Log { private static int s_level; public static void Write(string message) => Console.WriteLine(new string(' ', s_level * 2) + message); public static IDisposable Indent() { s_level++; return new Restore(); } private sealed class Restore : IDisposable { public void Dispose() => s_level--; } }
Deploying
Building
Compiling 12 files
Uploading
Doneawait using
Some resources need to do asynchronous work when they're released — flushing a network stream, say. They implement IAsyncDisposable, and you release them with await using (lesson 57 explains await):
await using (var stream = new FileStream("data.bin", FileMode.Create)) { await stream.WriteAsync(new byte[] { 1, 2, 3, 4 }); } Console.WriteLine(new FileInfo("data.bin").Length);
4
Try it yourself
Write a Timer helper that prints how long a block took when it's disposed. (Timings vary from run to run, so this one shows no fixed output.)