In some instances, throwing an exception and unwinding the stack can create a problem. For example, if you opened a file, connected to a database, or otherwise committed a resource, you might need an opportunity to close the file or database connection. As you saw in the previous examples, when an exception is thrown, it can leave behind code in the method that never gets executed. If that orphaned code is where you closed the file, your program could end without cleaning up after itself.
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 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 happens, the file could remain open. 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. If you have code that must run at the end of a method, no matter what exceptions are thrown, using a finally block will guarantee that the code will run
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_16_5_ _ _ _finally_Block { 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, we've removed one of the catch
blocks from Example 16-4 to save space, and added a finally
block. 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. We're leaving out the details of file manipulation to keep the example simple.