Reading — step 1 of 6
Learn
In .NET, the GC handles memory but NOT other resources — file handles, sockets, DB connections, locks. The IDisposable pattern is how you ensure these get cleaned up promptly.
The interface
public interface IDisposable
{
void Dispose();
}
Any class holding non-memory resources should implement IDisposable. The convention: callers MUST call Dispose() when done.
using statement — automatic disposal
using (var stream = File.OpenRead("data.txt"))
{
// use stream
} // stream.Dispose() called here, even on exception
The using block ensures Dispose runs no matter what — like try/finally. This is the DEFAULT pattern for any IDisposable.
using declarations (C# 8+)
The newer form scopes to the enclosing block:
void ReadFile()
{
using var stream = File.OpenRead("data.txt");
// ... do work ...
} // stream.Dispose() at end of method
No extra braces. Cleaner for single-resource cases.
Multiple resources
using var input = File.OpenRead("in.txt");
using var output = File.Create("out.txt");
input.CopyTo(output);
// both disposed in reverse declaration order
Or nested:
using (var conn = new SqlConnection(connStr))
using (var cmd = conn.CreateCommand())
{
// ...
}
Implementing IDisposable correctly
For classes that own resources:
public class TempFile : IDisposable
{
private readonly string path;
private bool disposed;
public TempFile()
{
path = Path.GetTempFileName();
}
public void Dispose()
{
if (disposed) return;
try { File.Delete(path); } catch { /* log */ }
disposed = true;
GC.SuppressFinalize(this);
}
}
The disposed flag ensures Dispose is idempotent. GC.SuppressFinalize tells the GC it doesn't need to call any finalizer (improves performance).
Grader note:
using varis C# 8+ syntax — the exercise grader runs Mono's C# 7 compiler, so write the classic block formusing (var r = new Resource()) { ... }in your solution.
IAsyncDisposable
For async cleanup (Stream.DisposeAsync, etc.):
await using var stream = File.OpenRead("data.txt");
// stream.DisposeAsync() called at end of scope
The await using runs the asynchronous Dispose. Useful for things that need to flush or commit asynchronously.
When to implement IDisposable
You need it when your class:
- Owns a
Stream,SqlConnection,HttpClient, etc. (anything IDisposable) - Holds OS handles (P/Invoke, file descriptors)
- Subscribes to long-lived events (so Dispose can unsubscribe)
- Acquires locks, semaphores, etc.
If your class only holds managed memory (strings, lists, etc.), you don't need IDisposable.
Common mistakes
- Forgetting
using— resources leak until GC runs Finalize, which may be never. - Disposing what you didn't create — only the owner should dispose. Borrowed instances stay open.
- Calling
Disposethen continuing to use — typically throwsObjectDisposedException. - Implementing IDisposable but not making Dispose idempotent — second call should be safe.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…