Reputation: 7868
Based on this post, I created a bridge between components for Windows forms and IDisposable objects. It pretty much looks like this:
namespace MyApp
{
public class Disposer: Component
{
private readonly Action<bool> _dispose;
public Disposer(Action<bool> disposeCallback)
{
if (disposeCallback == null)
throw new ArgumentNullException(nameof(disposeCallback));
this._dispose = disposeCallback;
}
protected override void Dispose(bool disposing)
{
this._dispose(disposing);
base.Dispose(disposing);
}
}
}
So far so good. Then I created unit tests, including one for the particular validation on the constructor argument.
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Disposer_ShouldNotAllowNullActions()
{
new Disposer(null);
}
Here's the catch: not only my test fails, but it actually gets aborted. The test platform itself crashes (ReSharper test runner). By digging into my Windows Event Viewer I could see that the Dispose() method is being called, and since this._dispose
is essentially null at this point, it fails with a NullReferenceException
.
I fixed this with providing an empty lambda as the default value.
But if the constructor throws an exception (which I confirmed it does), why is the Dispose
method called at all?
Upvotes: 2
Views: 290
Reputation: 152624
why is the
Dispose
method called at all?
The finalizer for a class is called even if the constructor throws an exception. The finalizer for Component
calls Dispose()
:
~Component() {
Dispose(false);
}
Since you override Dispose(bool)
, your override is called if the constructor throws an exception. Since this is a real possibility in your code, I'd suggest making sure both this
and this._dispose
are not null.
Upvotes: 1
Reputation: 26446
The Component
class must have a finalizer that calls this.Dispose()
, which causes your override to be called.
Finalizers are run even if the constructor did not complete - this allows all resources that were allocated before the constructor failed to be cleaned up.
Upvotes: 6