Implement IDisposable via Inheritance - Yortw/Yort.Trashy GitHub Wiki
Benefits
Using the disposable base classes or the DisposeAssistant from Yort.Trashy provides the following benefits;
- Reduces the amount of boiler plate required to implement IDisposable.
- Implements the dispose pattern correctly, including calling SuppressFinalize, ensuring unmanaged resources are disposed even if an error occurs during managed type disposal (including ThreadAbortException) etc.
- Ensures "idempotent" disposal, i.e calling Dispose only executes the dispose logic once, subsequent calls to Dispose do nothing
- Helps with thread-safety. The idempotency mentioned above remains true even if multiple threads try to simultaneously dispose an object. The first call will dispose the object, the others will wait for the first thread to finish then return without doing anything. Additionally you can use ObtainBusyToken or EnterBusy/ExitBusy to prevent calls to Dispose from executing while threads are calling other methods on the object.
- Integrated tracking of disposable types.
Managed Resources Only
Use DisposableManagedOnlyBase when
- Your class is sealed and only has managed resources to dispose (other types implementing IDisposable)
- Your class is unsealed, only has managed resources and you do not expect derived types to usually have unmanaged resources
public class MyDisposable : DisposableManagedOnlyBase
{
protected override void DisposeManagedResources()
{
//TODO: Write your dispose logic here
}
}
If you have a class that already inherits from DisposableManagedOnlyBase at some level, and your derived class has unmanaged resources then add a finalizer that calls Dispose(false) and override DisposeUnmanagedResources.
public class MyDisposable : DisposableManagedOnlyBase
{
protected override void DisposeManagedResources()
{
//TODO: Write your dispose logic here
}
protected override void DisposeUnmanagedResources()
{
//TODO: Write your dispose logic for unmanaged handles and resources here
}
~MyDisposable()
{
Dispose(false);
}
}
Managed & Unmanaged or Only Unmanaged Resources
Inherit from DisposableBase instead and just implement whichever overrides you need.
You can also do this if you expect derived types to commonly have unmanaged resources even if your class doesn't, the main difference is this base class includes a correct finalizer already. This incurs the performance hit a finalizer normally incurs, but saves derived types from having to implement one themselves.
public class MyDisposable : DisposableManagedOnlyBase
{
protected override void DisposeManagedResources()
{
//TODO: Write your dispose logic here
//Only override if you have managed resources to dispose.
}
protected override void DisposeUnmanagedResources()
{
//TODO: Write your dispose logic for unmanaged handles and resources here
//Only override if you have unmanaged resources to dispose.
}
}
Extras
Once you've inherited from one of the base classes there are a few methods that can help you with writing your other methods in the class.
Throw ObjectDisposedException When Disposed
You can call ThrowIfDisposed() at the top of your methods to check if the object is disposed and throw an ObjectDisposedException with the correct type information provided.
public void DoSomething()
{
ThrowIfDisposed();
// Your logic here.
}
Prevent Disposal While a Method Executes
To prevent another thread from disposing your object while your method is executing you can either use ObtainBusyToken (easiest) or Enter/ExitBusy.
Note, ObtainBusyToken and EnterBusy both call ThrowIfDisposed for you, so no need to do both.
Using a token;
public void DoSomething()
{
using (var busyToken = this.ObtainBusyToken()) //Disposal is blocked one this line executes.
{
// Your logic goes here.
} // Object allows disposable from this point on.
}
Using enter/exit busy;
public void DoSomething()
{
EnterBusy(); //Disposal is blocked one this line executes.
try
{
// Your logic goes here.
}
finally
{
ExitBusy(); // Object allows disposable from this point on.
}
}