In some instances, throwing an exception and unwinding the stack can create a problem. For example, if you opened a file or otherwise committed a resource, you might need an opportunity to close the file or flush the buffer.
If there is some action you must take
regardless of whether an exception is thrown, such as closing a file,
you have two strategies to choose from. One approach is to enclose the
dangerous action in a try
block and
then to perform the necessary action (close the file) in both the
catch
and try
blocks. However, this is an ugly
duplication of code, and it’s error prone. C# provides a better
alternative in the finally
block.
You create a finally
block with
the keyword finally
, and you enclose
the block in braces. The code in the finally
block is guaranteed to be executed
regardless of whether an exception is thrown. The TestFunc( )
method in the next listing, Example 16-5, simulates
opening a file as its first action. The method then undertakes some
mathematical operations, and then the file is closed.
A finally
block can be
created with or without catch
blocks, but a finally
block
requires a try
block to execute. It
is an error to exit a finally
block
with break
, continue
, return
, or goto
.
It is possible that sometime between opening and closing the file,
an exception will be thrown. If this were to occur, it would be possible
for the file to remain open. The developer knows that no matter what
happens, at the end of this method, the file should be closed, so the
file close function call is moved to a finally
block, where it is executed regardless
of whether an exception is thrown. Example 16-5 uses a finally
block.
Example 16-5. Using a finally block
using System; namespace UsingAFinallyBlock { class Tester { public void Run( ) { try { Console.WriteLine( "Open file here" ); double a = 5; double b = 0; Console.WriteLine( "{0} / {1} = {2}", a, b, DoDivide( a, b ) ); Console.WriteLine( "This line may or may not print" ); } // most derived exception type first catch ( DivideByZeroException ) { Console.WriteLine( "DivideByZeroException caught!" ); } catch { Console.WriteLine( "Unknown exception caught" ); } finally { Console.WriteLine( "Close file here." ); } } // do the division if legal public double DoDivide( double a, double b ) { if ( b == 0 ) throw new DivideByZeroException( ); if ( a == 0 ) throw new ArithmeticException( ); return a / b; } static void Main( ) { Console.WriteLine( "Enter Main..." ); Tester t = new Tester( ); t.Run( ); Console.WriteLine( "Exit Main..." ); } } }
The output looks like this:
Enter Main... Open file here DivideByZeroException caught! Close file here. Exit Main...
In Example 16-5,
one of the catch
blocks from Example 16-4 has been
eliminated to save space and a finally
block has been added. Whether or not
an exception is thrown, the finally
block is executed; thus, in both examples, the following message is
output:
Close file here.
Of course, in a real application, you would actually open the file
in the try
block, and you’d actually
close the file in the finally
block.
The details of file manipulation have been eliminated to keep the
example simple.