12.6. Finally BlockPrograms frequently request and release resources dynamically (i.e., at execution time). For example, a program that reads a file from disk first makes a file-open request (as we'll see in Chapter 18, Files and Streams). If that request succeeds, the program reads the contents of the file. Operating systems typically prevent more than one program from manipulating a file at once. Therefore, when a program finishes processing a file, the program should close the file (i.e., release the resource) so other programs can use it. If the file is not closed, a resource leak occurs. In such a case, the file resource is not available to other programs, possibly because a program using the file has not closed it. In languages such as C and C++, in which the programmer (not the runtime) is responsible for dynamic memory management, the most common type of resource leak is a memory leak. A memory leak occurs when a program allocates memory (as Visual Basic programmers do via keyword New), but does not deallocate the memory when it is no longer needed. Normally, this is not an issue in Visual Basic, because the CLR garbage collects memory that is no longer needed by an executing program (Section 9.9). However, other kinds of resource leaks (such as unclosed files) can occur. Error-Prevention Tip 12.2
Moving Resource Release Code to a Finally BlockTypically, exceptions occur when processing resources that require explicit release. For example, a program that processes a file might receive IOExceptions during the processing. For this reason, file processing code normally appears in a try block. Regardless of whether a program experiences exceptions while processing a file, the program should close the file when it is no longer needed. Suppose a program places all resource request and resource release code in a try block. If no exceptions occur, the TRy block executes normally and releases the resources after using them. However, if an exception occurs, the TRy block may expire before the resource-release code can execute. We could duplicate all the resource release code in each of the Catch blocks, but this would make the code more difficult to modify and maintain. We could also place the resource release code after the try statement; however, if the try block terminates due to a return statement, code following the try statement would never execute. To address these problems, Visual Basic's exception handling mechanism provides the optional Finally block, which is guaranteed to execute regardless of whether the TRy block executes successfully or an exception occurs. This makes the Finally block an ideal location in which to place resource-release code for resources that are acquired and manipulated in the corresponding TRy block. If the TRy block executes successfully, the Finally block executes immediately after the TRy block terminates. If an exception occurs in the try block, the Finally block executes immediately after a Catch block completes. If the exception is not caught by a Catch block associated with the try block, or if a Catch block associated with the TRy block throws an exception itself, the Finally block executes before the exception is processed by the next enclosing TRy block (if there is one). By placing the resource release code in a Finally block, we ensure that even if the program terminates due to an uncaught exception, the resource will be deallocated. Note that local variables in a try block cannot be accessed in the corresponding Finally block. For this reason, variables that must be accessed in both a try block and its corresponding Finally block should be declared before the TRy block. Error-Prevention Tip 12.3
Performance Tip 12.1
If one or more Catch blocks follow a try block, the Finally block is optional. However, if no Catch blocks follow a try block, a Finally block must appear immediately after the TRy block. If any Catch blocks follow a try block, the Finally block (if there is one) appears after the last Catch block. Only whitespace and comments can separate the blocks in a TRy statement. Common Programming Error 12.4
Demonstrating the Finally BlockThe application in Fig. 12.4 demonstrates that the Finally block always executes, regardless of whether an exception occurs in the corresponding TRy block. The program consists of method Main (lines 537) and four other methods that Main invokes to demonstrate Finally. These methods are DoesNotThrowException (lines 4051), THRowExceptionWithCatch (lines 5466), ThrowExceptionWithoutCatch (lines 6981) and ThrowExceptionCatchRethrow (lines 84103). Figure 12.4. Finally always executes, regardless of whether an exception occurs.
Line 8 of Main invokes method DoesNotThrowException. The try block for this method outputs a message (line 43). Because the try block does not throw any exceptions, program control ignores the Catch block (lines 4445) and executes the Finally block (lines 4647), which outputs a message. Program control continues with the first statement after end of the Finally block (line 50) which outputs a message indicating that the end of the method has been reached. Then, program control returns to line 11 of Main. Throwing Exceptions Using the Throw StatementLine 12 of Main invokes method ThrowExceptionWithCatch (lines 5466), which begins in its try block (lines 5658) by outputting a message. Next, the TRy block creates an Exception object and uses a Throw statement to throw the exception object (line 58). Executing the THRow statement indicates that an exception has occurred. So far you have caught exceptions thrown only by called methods. You can throw exceptions by using the Throw statement. Just as with exceptions thrown by the FCL's methods and the CLR, this indicates to client applications that an error has occurred. A Throw statement specifies an object to be thrown. The operand of a THRow statement can be of type Exception or of any type derived from class Exception. Common Programming Error 12.5
The String passed to the constructor becomes the exception object's error message. When a Throw statement in a try block executes, the TRy block expires immediately, and program control continues with the first matching Catch block (lines 5960) following the TRy block. In this example, the type thrown (Exception) matches the type specified in the Catch, so line 60 outputs a message indicating the exception that occurred. Then, the Finally block (lines 6162) executes and outputs a message. At this point, program control continues with the first statement after the end of the Finally block (line 65), which outputs a message indicating that the end of the method has been reached. Program control then returns to Main. In line 60, note that we use the exception object's Message property to retrieve the error message associated with the exception (i.e., the message passed to the Exception constructor). Section 12.7 discusses several properties of class Exception. Lines 1924 of Main define a try statement in which Main invokes method ThrowExceptionWithoutCatch (lines 6981). The try block enables Main to catch any exceptions thrown by THRowExceptionWithoutCatch. The try block in lines 7173 of THRowExceptionWithoutCatch begins by outputting a message. Next, the try block throws an Exception (line 73) and expires immediately. Normally, program control would continue at the first Catch following this try block. However, this try block does not have any Catch blocks. Therefore, the exception is not caught in method ThrowExceptionWithoutCatch. Program control proceeds to the Finally block (lines 7476), which outputs a message. At this point, program control returns to Mainany statements appearing after the Finally block (e.g., line 80) do not execute. In this example, such statements could cause logic errors, because the exception thrown in line 73 is not caught. In Main, the Catch block in lines 2123 catches the exception and displays a message indicating that the exception was caught in Main. Rethrowing ExceptionsLines 3136 of Main define a try statement in which Main invokes method ThrowExceptionCatchRethrow (lines 84103). The try statement enables Main to catch any exceptions thrown by ThrowExceptionCatchRethrow. The try statement in lines 8699 of ThrowExceptionCatchRethrow begins by outputting a message. Next, the try block throws an Exception (line 88). The try block expires immediately, and program control continues at the first Catch (lines 8995) following the try block. In this example, the type thrown (Exception) matches the type specified in the Catch, so line 90 outputs the exception's message, which in this case indicates where the exception was thrown. Line 93 uses the Throw statement to rethrow the exception. This indicates that the Catch block performed partial processing of the exception and now is passing the exception back to the calling method (in this case, Main) for further processing. You can also rethrow an exception with a version of the Throw statement which takes an operand that is the reference to the exception that was caught. It's important to note, however, that this form of THRow statement resets the throw point, so the original throw point's stack trace information is lost. Section 12.7 demonstrates using a Throw statement with an operand from a Catch block. In that section, you will see that after an exception is caught, you can create and throw a different type of exception object from the Catch block and you can include the original exception as part of the new exception object. Class library designers often do this to customize the exception types thrown from methods in their class libraries or to provide additional debugging information. The exception handling in method THRowExceptionCatchRethrow does not complete, because the the method rethrows the exception with the THRow statement in line 93. This causes method ThrowExceptionCatchRethrow to terminate and return control to Main. Once again, the Finally block (lines 9698) executes and outputs a message before control returns to Main. When control returns to Main, the Catch block in lines 3335 catches the exception and displays a message indicating that the exception was caught. Then the program terminates. Returning After a Finally BlockNote that the next statement to execute after a Finally block terminates depends on the exception-handling state. If the TRy block successfully completes, or if a Catch block catches and handles an exception, the program continues its execution with the next statement after the Finally block. However, if an exception is not caught, or if a Catch block rethrows an exception, program control continues in the next enclosing TRy block, which could be in the calling method or in one of its callers. It also is possible to nest a TRy statement in a TRy block; in such a case, the outer try statement's Catch blocks would process any exceptions that were not caught in the inner try statement. If a TRy block executes and has a corresponding Finally block, the Finally block executes even if the TRy block terminates due to a Return statementthe Return occurs after executing the Finally block. Common Programming Error 12.6
Error-Prevention Tip 12.4
Software Engineering Observation 12.2
The Using StatementRecall from earlier in this section that resource-release code should be placed in a Finally block to ensure that a resource is released, regardless of whether exceptions occurred when the resource was used in the corresponding TRy block. An alternative notationthe Using statementsimplifies writing code in which you obtain a resource, use the resource in a TRy block and release the resource in a corresponding Finally block. For example, a file-processing application (Chapter 18) could process a file with a Using statement to ensure that the file is closed properly when it is no longer needed. The resource must be an object that implements the IDisposable interface and therefore has a Dispose method. The general form of a Using statement is Using exampleObject As New ExampleObject() exampleObject.SomeMethod() End Using where ExampleObject is a class that implements the IDisposable interface. This code creates an object of type ExampleObject and uses it in a statement, then calls its Dispose method to release any resources used by the object. The Using statement implicitly places the code in its body in a TRy block with a corresponding Finally block that calls the object's Dispose method. For instance, the preceding code is equivalent to Dim exampleObject As New ExampleObject() Try exampleObject.SomeMethod() Finally If Not (exampleObject Is Nothing) Then exampleObject.Dispose() End If End Try Note that the If statement ensures that exampleObject still references an object; otherwise, a NullReferenceException might occur. You can read more about the Using statement in Section 10.13 of the Visual Basic Language Specification. |