Over the years I’ve implemented the Dispose pattern in C# more times than I can count. And if you’ve done the same, you know the drill: boilerplate, subtle rules, edge cases, async variants, thread‑safety, unmanaged resources, inheritance… and the constant risk of getting one tiny detail wrong.
So I decided to fix that.
Today I’m happy to introduce a new open‑source project:
ReflectionIT.DisposeGenerator
A C# Source Generator that automatically implements the full dispose and async‑dispose patterns for you — safely, consistently, and with zero boilerplate.
GitHub: https://github.com/sonnemaf/ReflectionIT.DisposeGenerator
Why another dispose helper?
Because the official dispose pattern is correct, but verbose.
Because async disposal adds even more complexity.
Because inheritance rules are easy to forget.
Because thread‑safety is tricky.
Because unmanaged resources require special handling.
And because source generators are now powerful enough to eliminate all of this ceremony.
With ReflectionIT.DisposeGenerator, you simply annotate your type and the generator produces all required members:
-
Dispose() -
Dispose(bool) -
DisposeAsync() -
DisposeAsyncCore() -
IsDisposed -
ThrowIfDisposed() - Finalizer (when needed)
- Thread‑safe disposal logic (optional)
- Unmanaged resource cleanup hooks
- Support for derived classes
All generated code follows the official .NET guidelines.
Installation
Available on NuGet:
<PackageReference Include="ReflectionIT.DisposeGenerator" Version="*" />
How it works
You start by marking your partial class or struct with the [Disposable] attribute and annotate the fields or properties that should be disposed using [Dispose] or [AsyncDispose].
using System;
using System.IO;
using ReflectionIT.DisposeGenerator.Attributes;
[Disposable]
public partial class LogWriter : IDisposable
{
[Dispose]
private readonly StreamWriter _streamWriter;
public LogWriter(string path) =>
_streamWriter = new StreamWriter(path);
public void WriteLine(string text)
{
ThrowIfDisposed();
_streamWriter.WriteLine($"{DateTime.Now}\t{text}");
}
}
That’s it.
The generator produces the full, correct implementation behind the scenes.
Key features
✔ Automatic dispose pattern generation
Just annotate your type and members — the generator handles the rest.
✔ Sync and async disposal
Use [Dispose] for Dispose() and [AsyncDispose] for DisposeAsync().
You can even combine them on the same member.
✔ ThrowIfDisposed() and IsDisposed
Generated automatically to help you fail fast when an object is used after disposal.
✔ Optional thread‑safe disposal
Enable with IsThreadSafe = true to use Interlocked.CompareExchange.
✔ Unmanaged resource support
Set HasUnmanagedResources = true and implement the generated partial method ReleaseUnmanagedResources().
✔ Inheritance‑friendly
Use OverrideDispose = true or OverrideDisposeAsyncCore = true when deriving from a type that already implements disposal.
✔ Diagnostics
If you forget to mark your type as partial, you’ll get a clear compiler diagnostic (RITDG001).
Example: async disposal
[Disposable]
public partial class LogWriter : IAsyncDisposable
{
[AsyncDispose]
private readonly StreamWriter _streamWriter;
public LogWriter(string path) =>
_streamWriter = new StreamWriter(path);
}
The generator creates both DisposeAsync() and DisposeAsyncCore() for you.
Example: unmanaged resources
[Disposable(HasUnmanagedResources = true)]
public partial class LogWriterWithPtr : IDisposable
{
private readonly IntPtr _pointer;
[Dispose]
private StreamWriter StreamWriter { get; }
public LogWriterWithPtr(string path)
{
StreamWriter = new StreamWriter(path);
_pointer = Marshal.AllocHGlobal(128);
}
protected virtual partial void ReleaseUnmanagedResources() =>
Marshal.FreeHGlobal(_pointer);
}
The generator adds a finalizer and calls your partial method at the right moment.
Why I built this
I’ve been teaching and writing about .NET for years, and I’ve seen many developers struggle with the dispose pattern — especially when async disposal and inheritance come into play. Source generators offer a clean, compile‑time solution that keeps your codebase tidy while ensuring correctness.
This project aims to:
- reduce boilerplate
- prevent subtle bugs
- encourage correct disposal patterns
- make async disposal easier
- support advanced scenarios without complexity
And of course: it’s open source, MIT‑licensed, and contributions are welcome.
Try it out and share feedback
You can find the full source, documentation, and examples on GitHub:
https://github.com/sonnemaf/ReflectionIT.DisposeGenerator
If you try it in your own projects, I’d love to hear your experiences — issues, ideas, and PRs are all appreciated.
Happy coding!
All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.
Blog comments
0 responses