For the equivalent mechanism in C++ (the destructor), the advice is that it should usually not throw any exceptions. This is mainly because by doing so you might terminate your process, which is only very rarely a good strategy.
In the equivalent scenario in .NET ...
- A first exception is thrown
- A finally block is executed as a result of the first exception
- The finally block calls a Dispose() method
- The Dispose() method throws a second exception
... your process does not terminate immediately. However, you lose information because .NET unceremoneously replaces the first exception with the second one. A catch block somewhere up the call stack will therefore never see the first exception. However, one is usually more interested in the first exception because that normally gives better clues as to why things started to go wrong.
Since .NET lacks a mechanism to detect whether code is being executed while an exception is pending, it seems there are really only two choices how IDisposable can be implemented:
- Always swallow all exceptions that occur inside Dispose(). Not good as you might also end up swallowing OutOfMemoryException, ExecutionEngineException, etc. which I'd usually rather let tear down the process when they occur without another exception already pending.
- Let all exceptions propagate out of Dispose(). Not good as you might lose information about the root cause of a problem, see above.
So, which is the lesser of the two evils? Is there a better way?
EDIT: To clarify, I'm not talking about actively throwing exceptions from Dispose() or not, I'm talking about letting exceptions thrown by methods called by Dispose() propagate out of Dispose() or not, for example:
using System;
using System.Net.Sockets;
public sealed class NntpClient : IDisposable
{
private TcpClient tcpClient;
public NntpClient(string hostname, int port)
{
this.tcpClient = new TcpClient(hostname, port);
}
public void Dispose()
{
// Should we implement like this or leave away the try-catch?
try
{
this.tcpClient.Close(); // Let's assume that this might throw
}
catch
{
}
}
}
There are various strategies for propagating or swallowing exceptions from the
Dispose
method, possibly based on whether an unhanded exception was also thrown from the main logic. The best solution would be to leave the decision up to the caller, depending on their specific requirements. I have implemented a generic extension method that does this, offering:using
semantics of propagatingDispose
exceptionsDispose
exceptionsDispose
exceptions when there is an exception from the main logic that would otherwise be lostAggregateException
AggregateException
(likeTask.Wait
does)This is my extension method:
These are the implemented strategies:
Sample use:
Update: If you need to support delegates that return values and/or are asynchronous, then you could use these overloads:
Dispose
should be designed to do its purpose, disposing the object. This task is safe and does not throw exceptions most of the time. If you see yourself throwing exceptions fromDispose
, you should probably think twice to see if you are doing too much stuff in it. Beside that, I thinkDispose
should be treated like all other methods: handle if you can do something with it, let it bubble if you can't.EDIT: For the specified example, I would write the code so that my code does not cause an exception, but clearing the
TcpClient
up might cause an exception, which should be valid to propagate in my opinion (or to handle and rethrow as a more generic exception, just like any method):However, just like any method, if you know
tcpClient.Close()
might throw an exception that should be ignored (doesn't matter) or should be represented by another exception object, you might want to catch it.Here is a way to fairly cleanly grab any exceptions thrown by the contents of the
using
or theDispose
.Original code:
Then here is code that will throw if either
codeInUsing()
throws orfoo.Dispose()
throws or both throw, and let you see the first exception (sometimes wrapped as an InnerExeption, depending):It's not great but not too bad.
Here is the code to implement this. I have it set so that it only works as described when the debugger is not attached, because when the debugger is attached I am more concerned that it will break in the right place on the first exception. You could modify as needed.
The Framework Design Guidelines (2nd ed) has this as (§9.4.1):
Commentary [Edit]:
Dispose(bool)
may be called from the finaliser, throwing from a finaliser is a bad idea and will block other objects from being finalised.My view: exceptions escaping from Dispose should only be those, as in the guideline, that as sufficiently catastrophic that no further reliable function is possible from the current process.
It's too bad Microsoft didn't provide an Exception parameter to Dispose, with the intention that it be wrapped as an InnerException in case disposal itself throws an exception. To be sure, effective use of such a parameter would require use of an exception-filter block, which C# doesn't support, but perhaps the existence of such a parameter could have motivated the C# designers into providing such a feature? One nice variation I'd like to see would be the addition of an Exception "parameter" to a Finally block, e.g.
which would behave like a normal Finally block except that 'ex' would be null/Nothing if the 'Try' ran to completion, or would hold the thrown exception if it did not. Too bad there's no way to make existing code use such a feature.
Releasing resources should be a "safe" operation - after all how can I recover from not being able to release a resource? so throwing an exception from Dispose just doesn't make sense.
However, if I discover inside Dispose that program state is corrupted it's better to throw the exception then to swallow it, its better to crush now then to continue running and produce incorrect results.