Section 12.6. Finally Block


12.6. Finally Block

Programs 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

The CLR does not completely eliminate memory leaks. The CLR will not garbage collect an object until the program contains no more references to that object. Thus, memory leaks can occur if you inadvertently keep references to unwanted objects.


Moving Resource Release Code to a Finally Block

Typically, 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

A Finally block typically contains code to release resources acquired in the corresponding TRy block, which makes the Finally block an effective mechanism for eliminating resource leaks.


Performance Tip 12.1

As a rule, resources should be released as soon as they are no longer needed in a program. This makes them available for reuse promptly.


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

Placing the Finally block before a Catch block is a syntax error.


Demonstrating the Finally Block

The 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.

  1  ' Fig. 12.4: UsingExceptions.vb  2  ' Using finally blocks.  3  ' Demonstrate that finally always executes.  4  Module UsingExceptions  5     Sub Main()  6        ' Case 1: No exceptions occur in called method  7        Console.WriteLine("Calling DoesNotThrowException")  8        DoesNotThrowException()  9 10        ' Case 2: Exception occurs and is caught in called method 11        Console.WriteLine(vbCrLf & "Calling ThrowExceptionWithCatch") 12        ThrowExceptionWithCatch() 13 14        ' Case 3: Exception occurs, but is not caught in called method 15        ' because there is no catch block. 16        Console.WriteLine(vbCrLf & "Calling ThrowExceptionWithoutCatch") 17 18        ' call ThrowExceptionWithoutCatch 19        Try 20           ThrowExceptionWithoutCatch() 21        Catch 22           Console.WriteLine("Caught exception from " & _ 23              "ThrowExceptionWithoutCatch in Main") 24        End Try 25 26        ' Case 4: Exception occurs and is caught in called method, 27        ' then rethrown to caller. 28        Console.WriteLine(vbCrLf & "Calling ThrowExceptionCatchRethrow") 29 30        ' call ThrowExceptionCatchRethrow 31        Try 32           ThrowExceptionCatchRethrow() 33        Catch 34           Console.WriteLine("Caught exception from " & _ 35              "ThrowExceptionCatchRethrow in Main") 36        End Try 37     End Sub ' Main 38 39     ' no exceptions thrown 40     Sub DoesNotThrowException() 41        ' try block does not throw any exceptions 42        Try 43           Console.WriteLine("In DoesNotThrowException") 44        Catch 45           Console.WriteLine("This catch never executes") 46        Finally 47           Console.WriteLine("finally executed in DoesNotThrowException") 48        End Try                                                           49 50        Console.WriteLine("End of DoesNotThrowException") 51     End Sub ' DoesNotThrowException 52 53     ' throws exception and catches it locally 54     Sub ThrowExceptionWithCatch() 55        ' try block throws exception 56        Try 57           Console.WriteLine("In ThrowExceptionWithCatch") 58           Throw New Exception("Exception in ThrowExceptionWithCatch") 59        Catch exceptionParameter As Exception 60           Console.WriteLine("Message: " & exceptionParameter.Message) 61        Finally                                                             62           Console.WriteLine("finally executed in ThrowExceptionWithCatch") 63        End Try 64 65        Console.WriteLine("End of ThrowExceptionWithCatch") 66     End Sub ' ThrowExceptionWithCatch 67 68     ' throws exception and does not catch it locally 69     Sub ThrowExceptionWithoutCatch() 70        ' throw exception, but do not catch it 71        Try 72           Console.WriteLine("In ThrowExceptionWithoutCatch") 73           Throw New Exception("Exception in ThrowExceptionWithoutCatch") 74        Finally                                                           75           Console.WriteLine("finally executed in " & _                   76              "ThrowExceptionWithoutCatch")                               77        End Try                                                           78 79        ' unreachable code; logic error 80        Console.WriteLine("End of ThrowExceptionWithoutCatch") 81     End Sub ' ThrowExceptionWithoutCatch 82 83     ' throws exception, catches it and rethrows it 84     Sub ThrowExceptionCatchRethrow() 85        ' try block throws exception 86        Try 87           Console.WriteLine("In ThrowExceptionCatchRethrow") 88           Throw New Exception("Exception in ThrowExceptionCatchRethrow") 89        Catch exceptionParameter As Exception 90           Console.WriteLine("Message: " & exceptionParameter.Message) 91 92           ' rethrow exception for further processing 93           Throw                                      94 95           ' code placed here would be unreachable; logic error 96        Finally                                         97           Console.WriteLine("finally executed in " & _ 98              "ThrowExceptionCatchRethrow" )            99        End Try                                         100 101       ' any code placed here is never reached 102       Console.WriteLine("End of ThrowExceptionCatchRethrow") 103    End Sub ' ThrowExceptionCatchRethrow 104 End Module ' UsingExceptions 

[View full width]

Calling DoesNotThrowException In DoesNotThrowException finally executed in DoesNotThrowException End of DoesNotThrowException Calling ThrowExceptionWithCatch In ThrowExceptionWithCatch Message: Exception in ThrowExceptionWithCatch finally executed in ThrowExceptionWithCatch End of ThrowExceptionWithCatch Calling ThrowExceptionWithoutCatch In ThrowExceptionWithoutCatch finally executed in ThrowExceptionWithoutCatch Caught exception from ThrowExceptionWithoutCatch in Main Calling ThrowExceptionCatchRethrow In ThrowExceptionCatchRethrow Message: Exception in ThrowExceptionCatchRethrow finally executed in ThrowExceptionCatchRethrow Caught exception from ThrowExceptionCatchRethrow in Main



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 Statement

Line 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

It is a compilation error if the argument of a THRowan exception objectis not of class Exception or one of its derived classes.


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 Exceptions

Lines 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 Block

Note 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

Throwing an exception from a Finally block can be dangerous. If an uncaught exception is awaiting processing when the Finally block executes, and the Finally block throws a new exception that is not caught in the Finally block, the first exception is lost, and the new exception is passed to the next enclosing try block.


Error-Prevention Tip 12.4

When placing code that can throw an exception in a Finally block, always enclose the code in a try statement that catches the appropriate exception types. This prevents the loss of any uncaught and rethrown exceptions that occur before the Finally block executes.


Software Engineering Observation 12.2

Do not place TRy blocks around every statement that might throw an exception, because this can make programs difficult to read. It is better to place one try block around a significant portion of code, and follow this try block with Catch blocks that handle each of the possible exceptions. Then follow the Catch blocks with a single Finally block. Separate TRy blocks should be used when it is important to distinguish between multiple statements that can throw the same exception type.


The Using Statement

Recall 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.




Visual BasicR 2005 for Programmers. DeitelR Developer Series
Visual Basic 2005 for Programmers (2nd Edition)
ISBN: 013225140X
EAN: 2147483647
Year: 2004
Pages: 435

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net