A comprehensive .NET library providing useful IDisposable and IAsyncDisposable implementations for common resource management patterns.
- Empty Disposable - No-op singleton for null object pattern
- Action Disposable - Execute a delegate on dispose (idempotent)
- Aggregate Disposable - Wrapper with replaceable underlying resource
- Collection Disposable - Manage multiple disposables with LIFO disposal order
- Context Disposable - Dispose on a specific
SynchronizationContext(e.g., UI thread) - Shared Reference - Reference-counted resource sharing with lease pattern
All implementations have async counterparts (IAsyncDisposable):
AsyncDisposable.Empty,Create(),Aggregate(),Collection(),Shared()AsyncDisposableAdapter- WrapsIDisposableasIAsyncDisposable
DisposeAsyncIfAvailable()- CallsDisposeAsyncif available, otherwiseDisposeDisposeAll()/DisposeAllAsync()- Dispose all items in a collection (LIFO order)AsSharedReference()- Convert a disposable to a shared reference with leasing
| Feature | Sync API | Async API |
|---|---|---|
| Empty (no-op) | Disposable.Empty |
AsyncDisposable.Empty |
| Action callback | Disposable.Create(action) |
AsyncDisposable.Create(func) |
| Aggregate wrapper | Disposable.Aggregate(disposable) |
AsyncDisposable.Aggregate(disposable) |
| Collection | Disposable.Collection(items) |
AsyncDisposable.Collection(items) |
| Shared reference | SharedReference.Create(value) |
AsyncSharedReference.Create(value) |
| Context disposal | Disposable.Context(disposable, ctx) |
— |
| Adapt sync→async | — | AsyncDisposable.Adapt(disposable) |
// Singleton instance - useful as default/placeholder
IDisposable disposable = Disposable.Empty;
disposable.Dispose(); // no-op// Execute cleanup logic on dispose (idempotent - runs only once)
IDisposable disposable = Disposable.Create(() => Console.WriteLine("Disposed!"));
disposable.Dispose(); // prints "Disposed!"
disposable.Dispose(); // no-op// Wrapper allowing the underlying resource to be swapped
var aggregate = Disposable.Aggregate(initialResource);
aggregate.Disposable = newResource; // swap resource
aggregate.Dispose(); // disposes current resource// Manage multiple disposables - disposed in reverse (LIFO) order
var collection = Disposable.Collection(resource1, resource2, resource3);
collection.Add(resource4);
collection.Remove(resource2); // removed items are NOT disposed
collection.Dispose(); // disposes: resource4, resource3, resource1// Share a resource with reference counting
IDisposable resource = CreateExpensiveResource();
var lease1 = SharedReference.Create(resource);
var lease2 = lease1.AddReference();
var lease3 = lease2.AddReference();
lease1.Value.DoWork();
lease1.Dispose();
lease2.Value.DoWork();
lease2.Dispose();
lease3.Value.DoWork();
lease3.Dispose(); // resource disposed here (ref count = 0)// Dispose on a specific SynchronizationContext (e.g., UI thread)
var context = SynchronizationContext.Current;
var disposable = Disposable.Context(uiResource, context, async: false);
disposable.Dispose(); // runs on the context's thread// Wrap IDisposable as IAsyncDisposable
IDisposable syncResource = CreateResource();
IAsyncDisposable asyncResource = AsyncDisposable.Adapt(syncResource);
await asyncResource.DisposeAsync();// Dispose async if available, otherwise sync
await disposable.DisposeAsyncIfAvailable();
// Dispose all items in a collection (LIFO order)
var items = new object[] { resource1, resource2, nonDisposable, resource3 };
items.DisposeAll(); // disposes only IDisposable items, in reverse order
// Convert to shared reference
using var lease = myDisposable.AsSharedReference();Please provide any feedback, comments, or issues to this GitHub project here.
- v1.0.0 - Initial release
- v2.0.2 - Port to .NET Core/Standard
- v3.0.0 - Port to .NET 8.0 and refactor shared reference implementation
- v3.0.1 - Updated xml documentation
- v3.1.0 - Split ISharedReference into ISharedReferenceScope and ISharedReferenceProvider
- v4.0.0 - Revert the split
- v4.1.0 - Added async support
- v4.2.0 - Added async adapter
- v4.3.0 - Added DisposeAsyncIfAvailable extension. Added
idempotentoption to certain methods. - v4.4.0 - Refactored the
idempotentoption to use function overloads. - v5.0.0 - Refactored shared references/leases to use structs
- v5.0.1 - Removing dead code
- v5.0.2 - Removing more dead code
- v5.1.0 - Allow creating of async shared references without requiring async callsite
- v5.1.1 - Minor resharper cleanup
- v5.2.1 - Collections can be any object instead of just IDisposable or IAsyncDisposable
- v5.3.0 - .NET 10 upgrade