Reading — step 1 of 5
Learn
~1 min readAsync Patterns and Disposal
IDisposable is C#'s contract for deterministic cleanup of unmanaged resources (file handles, connections, locks).
class FileWrapper : IDisposable {
private FileStream stream;
private bool disposed = false;
public FileWrapper(string path) {
stream = new FileStream(path, FileMode.Open);
}
public void Dispose() {
if (!disposed) {
stream?.Dispose();
disposed = true;
GC.SuppressFinalize(this);
}
}
}
using statement — guarantees Dispose is called:
using (var file = new FileWrapper("data.txt")) {
// use file
} // Dispose() called here, even on exception
using declaration (C# 8+) — disposes at end of enclosing scope:
void Process() {
using var file = new FileWrapper("data.txt");
// use file — disposed when Process() returns
}
The full Dispose pattern for classes that have unmanaged resources AND need finalization safety:
class Resource : IDisposable {
private bool disposed = false;
private IntPtr unmanaged;
private SqlConnection conn;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposed) return;
if (disposing) {
// Free managed resources
conn?.Dispose();
}
// Free unmanaged resources
Marshal.FreeHGlobal(unmanaged);
disposed = true;
}
~Resource() {
Dispose(false); // finalizer — only runs if Dispose wasn't called
}
}
99% of code doesn't need finalizers. Just implement Dispose() and use using. Finalizers add GC overhead and run on a single thread.
IAsyncDisposable (C# 8+) — async cleanup:
class AsyncResource : IAsyncDisposable {
public async ValueTask DisposeAsync() {
await connection.CloseAsync();
}
}
await using (var r = new AsyncResource()) {
// use r
}
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…