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
{
}
}
}