-
Notifications
You must be signed in to change notification settings - Fork 1
Disposable
This class is a part of the Components library.
It is an abstract base class that you may inherit from in your own classes to easily implement disposal logic.
It supports the IDisposable paradigm already present in the .NET BCL, but also provides for asynchronous disposal.
- Virtual Members of Disposable
- Synchronous Disposal
- Asynchronous Disposal
- Disposal Status
- Added Benefits
Gear.Components.Disposable provides four virtual members that are intended to be overridden by implementers (you):
-
void Dispose(bool disposing)- the logic to execute when disposing of your class synchronously -
Task DisposeAsync(bool disposing, CancellationToken canecallationToken)- the the logic to execute when disposing of your class asynchronously -
bool IsDisposable { get }- lets the base class know that your class is designed to be disposed of synchronously (the default implementation returnsfalse) -
bool IsAsyncDisposable { get }- lets the base class know that your class is designed to be disposed of asynchronously (the default implementation returnsfalse)
To implement synchronous disposal, make sure your class inherits (ultimately) from Gear.Components.Disposable, and then override the Dispose method and the IsDisposable property.
class MyClass : Gear.Components.Disposable
{
protected override void Dispose(bool disposing)
{
// TODO: release unmanaged resources
if (disposing)
{
// TODO: additional logic only executed when NOT being finalized
}
}
protected override bool IsDisposable => true;
}Note: If the Dispose method throws an exception, the object will not be considered disposed.
When you are using a class that implements synchronous disposal, employ the C# using statement.
using (disposableObject)
{
// disposableObject will be disposed once execution exits this block
}Warning: If the class does not implement synchronous disposal, an InvalidOperationException will be thrown by Dispose when the CLR invokes it.
To implement asynchronous disposal, make sure your class inherits (ultimately) from Gear.Components.Disposable, and then override the DisposeAsync method and the IsAsyncDisposable property.
class MyClass : Gear.Components.Disposable
{
protected override async Task DisposeAsync(bool disposing, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// TODO: release unmanaged resources
if (disposing)
{
// TODO: additional logic only executed when NOT being finalized
}
}
protected override bool IsAsyncDisposable => true;
}We recommend that you also furnish a synchronous version when possible.
If you do not, the base class will be forced to block the thread executing the finalizer while explicitly waiting for the DisposeAsync method to execute (which is generally considered undesirable) to ensure disposal still occurs even in the absence of a synchronous implementation.
Note: If the DisposeAsync method throws an exception, the object will not be considered disposed.
When you are using a class that implements asynchronous disposal, you may call the static UsingAsync method.
await Gear.Components.Disposable.UsingAsync(disposableObject, () =>
{
// disposableObject will be disposed once execution exits this anonymous method
});It is also possible to provide an asynchronous method.
await Gear.Components.Disposable.UsingAsync(disposableObject, async () =>
{
// disposableObject will be disposed once execution exits this anonymous method
});Warning: If the class does not implement asynchronous disposal, an InvalidOperationException will be thrown by DisposeAsync when UsingAsync invokes it.
There are extensions available to make this syntax a little easier to deal with.
using Gear.Components;await disposableObject.UsingAsync(() =>
{
// disposableObject will be disposed once execution exits this anonymous method
});All overloads of UsingAsync (including the extension methods) will accept a cancellation token as an optional argument.
using (var cancellationTokenSource = new CancellationTokenSource(500))
await Gear.Components.Disposable.UsingAsync(disposableObject, () =>
{
// disposableObject will be disposed once execution exits this anonymous method
}, cancellationTokenSource.Token);When provided a cancellation token, UsingAsync will only throw an OperationCancelledException if cancellation has been requested and then one of the following occurs (in order of possibility):
- the method passed to
UsingAsynccalls the cancellation token'sThrowIfCancellationRequestedmethod - the disposal lock cannot be immediately acquired (something else is currently already trying to dispose of the class)
- the
DisposeAsyncimplementation calls the cancellation token'sThrowIfCancellationRequestedmethod
To determine whether the class has been disposed, get the value of the IsDisposed property.
This property is not available to outside callers, but you can hide the property with your own if you want to expose it.
class MyClass : Gear.Components.Disposable
{
new public bool IsDisposed => base.IsDisposed;
}If you want to ensure that certain operations cannot occur after the class has been disposed, call the ThrowIfDisposed method before attempting the operations.
Gear.Components.Disposable has the following functionality implemented for you already:
- garbage collector invocation of the finalizer will be suppressed once disposal has succeeded
- when the finalizer is called by the garbage collector because your class was not explicitly disposed, synchronous disposal will be used first if available according to
IsDisposable, and if not, but asynchronous disposal is available according toIsAsyncDisposable, the finalizer will block while callingDisposeAsync - whether disposal has occurred successfully is tracked internally and subsequent attempts to dispose of your class will be ignored
- disposal methods (
void Dispose()andTask DisposeAsync(CancellationToken cancellationToken)) employ locking to ensure that one and only one thread (or async execution context) can be attempting to dispose of your class at a time