Will Try / Finally (without the Catch) bubble the exception?
Solution 1:
Yes, it absolutely will. Assuming your finally
block doesn't throw an exception, of course, in which case that will effectively "replace" the one that was originally thrown.
Solution 2:
Any thoughts on the practice in general?
Yes. Be careful. When your finally block is running, it is entirely possible that it is running because an unhandled, unexpected exception has been thrown. That means that something is broken, and something completely unexpected could be happening.
In that situation it is arguably the case that you shouldn't run code in finally blocks at all. The code in the finally block might be built to assume that subsystems it depends upon are healthy, when in fact they could be deeply broken. The code in the finally block could make things worse.
For example, I often see this sort of thing:
DisableAccessToTheResource();
try
{
DoSomethingToTheResource();
}
finally
{
EnableAccessToTheResource();
}
The author of this code is thinking "I am making a temporary mutation to the state of the world; I need to restore the state to what it was before I was called". But let's think about all the ways this could go wrong.
First, access to the resource could already be disabled by the caller; in that case, this code re-enables it, possibly prematurely.
Second, if DoSomethingToTheResource throws an exception, is the right thing to do to enable access to the resource??? The code that manages the resource is unexpectedly broken. This code says, in effect "if the management code is broken, make sure that other code can call that broken code as soon as possible, so that it can fail horribly too." This seems like a bad idea.
Third, if DoSomethingToTheResource throws an exception, then how do we know that EnableAccessToTheResource will not also throw an exception? Whatever awfulness befell the use of the resource might also affect the cleanup code, in which case the original exception will be lost and the problem will be harder to diagnose.
I tend to write code like this without using try-finally blocks:
bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
EnableAccessToTheResource();
Now the state is not mutated unless it needs to be. Now the caller's state is not messed around with. And now, if DoSomethingToTheResource fails, then we do not re-enable access. We assume that something is deeply broken and do not risk making the situation worse by trying to keep running code. Let the caller deal with the problem, if they can.
So when is it a good idea to run a finally block? First, when the exception is expected. For example, you might expect that an attempt to lock a file might fail, because someone else has it locked. In that case it makes sense to catch the exception and report it to the user. In that case the uncertainty about what is broken is reduced; you are unlikely to make things worse by cleaning up.
Second, when the resource you are cleaning up is a scarce system resource. For example, it makes sense to close a file handle in a finally block. (A "using" is of course just another way of writing a try-finally block.) The contents of the file might be corrupt, but there's nothing you can do about that now. The file handle is going to be closed eventually, so it might as well be sooner rather than later.