Understanding Finally Blocks

Whether you use C# or VB, you’ll no doubt be familiar with the Try...Catch block:

Try
'Do stuff
Catch ex As Exception
'Handle exception
Finally
'Clean up
End Try

The basic idea is simple: any code you right that might go wrong should be inside a Try block.  You add a Catch block to handle the exception, and you add a Finally block to run any code you want to execute once everything is finished, regardless of how things went.  Typically, this is code to clean up after yourself.  Of course, you don’t need both the Catch or Finally blocks; you could use just one or the other.  Let’s look at at really simple example:

'Get a temporary file path
Dim sFile As String = IO.Path.GetTempFileName()

Try

'Create temp file
IO.File.WriteAllText(sFile, Now)

'TODO: Stuff
Throw New NotImplementedException()

'Delete temp file
IO.File.Delete(sFile)

Catch ex As Exception

'Write this error to a log file
IO.File.WriteAllText("errors.log", ex.ToString)

End Try

Hopefully, you all see the big problem here: since an exception is thrown, code will jump to the Try block and then carry on running the program.  Our temporary file will never be deleted.  That’s bad.  Instead, you should have used the Finally block, like this:

'Get a temporary file path
Dim sFile As String = IO.Path.GetTempFileName()

Try

'Create temp file
IO.File.WriteAllText(sFile, Now)

'TODO: Stuff
Throw New NotImplementedException()

Catch ex As Exception

'Write this error to a log file
IO.File.WriteAllText("errors.log", ex.ToString)

Finally

'Delete temp file
IO.File.Delete(sFile)

End Try

Let’s examine the Finally block a bit closer.  What exactly is the point of it?  If you read the manual, it basically says that code in the finally block executes after the code in both the Try and Catch runs, regardless of whether an error happened or not. Okay, so it’s pretty clear that you shouldn’t put your clean-up code in the Try block, because if an exception is run, execution jumps to the Catch block and then exits, so your clean-up code will never run.  But here’s the question many people don’t get: why not just put the clean up code after the whole block?  Won’t it basically do the same thing?  Well, most of the time, yes.  In fact, sometimes that’s exactly where you should put your next bit of code.  But sometimes, there’s a difference.

For example, what if we ran this program from a CD, or from another location where we have only read access?  Well, our exception handling code is going to break since it won’t have permission to write to “errors.log”.  If you put the File.Delete call after the whole Try block, it will never run.  Because it’s in the Finally block, though, it will run no matter what.

Of course, there’s another potential problem here, too: what if an exception is thrown when trying to write to the temp file (due to a disk error, perhaps)?  Well, in that case, your File.Delete call is going to break.  So what happens when an exception is thrown in a Finally block?  Actually, the same thing as any other exception: it will filter up to the next Try block, wherever that may be.  The way to deal with this would be to wrap your .Delete call in its own Try block – yes, you can embed Try blocks inside other Try blocks, even in the Finally portion.

There’s one other concept you might want to understand, and that is the Try...Finally block.  Why might you not want to have a Catch block?  Well, what if you have code higher up ready to handle any exceptions that might occur, but you still want to run some clean up code?  In this case, you’d put your clean-up code in the Finally block.  That code will run, but the code after the Try block won’t.  It’s kind of like having a chance to fix any problems your exception may have left without having to actually handle the exception at the time.

The Try block is a very powerful tool, but it’s important to understand exactly how it works and use it properly.  Since it only comes into play when things go wrong, and you never really know how or when things will go wrong, it’s something you MUST get right.

No comments:

Post a Comment


Copyright © 2010 Paul Guenette and Matthew Sleno.