Opposing market and technological forces are imposing a mild sort of schizophrenia on Visual Basic. Some want Visual Basic .NET to remain as much like VB6 as possible, so they can use forms without invoking the constructor and the new operator in VB.NET 2.0. At the same time, others want to drag VB .NET into the 21st century by adding constructs like the using operator introduced with C#.
No matter which side you are on, if you want to use VB.NET, you have to actually know what is in the shipped product and how to use it. This article demonstrates the general utility of the using block for VB .NET 2.0.
The .NET languages are garbage collected, meaning they automatically recover memory. Although garbage collection is optimized and reliable, it does not determine when memory and resources are collected. For this reason, classes that use finite resources that need some deterministic cleanup need to be protected by try-finally blocks.
The procedure of a try-finally block is to create a resource, use the resource in the try block, and clean up the resource in the finally block. The code in a finally block is always executed, even if an exception or a return statement occurs in the try block. Listing 1 shows how you can use an OpenFileDialog and a try-finally block to open a file, read it, and ensure the file is closed:
Listing 1: Protecting a File Resource with a Try-Finally Block
If (OpenFileDialog1.ShowDialog() = DialogResult.OK) Then Dim reader As StreamReader = _ New StreamReader(OpenFileDialog1.FileName) Try TextBox1.Text = reader.ReadToEnd Finally reader.Close() End Try End If
(This fragment assumes you have imported the System.IO namespace and have added an OpenFileDialog control.)
Notice that the StreamReader is created before the try block is entered. The finally block protects the opened and created stream only after the try block is entered, which assumes the StreamReader was created and opened successfully.
Many classes like StreamReader that have finite resources implement IDisposable. IDisposable requires a Dispose method, and Dispose can be used to perform clean up tasks like closing the stream (and releasing the file handle) for the StreamReader.
Working with the Using Block
The using block is a shorthand version of try-finally. Just like try-finally, using protects resources if the class containing the protected resource implements IDisposable. For example, StreamReader implements IDisposable and StreamReader's dispose method calls the close method. Thus, whether or not an exception occurs within the using block, the using block ensures that StreamReader's dispose method and consequently the close method is always called. Listing 2 demonstrates how to use the StreamReader in a using block:
Listing 2: The Using Block Is a Shorthand Version of the Try-Finally Block
If (OpenFileDialog1.ShowDialog() = _ Windows.Forms.DialogResult.OK) Then Using reader As StreamReader = _ New StreamReader(OpenFileDialog1.FileName) TextBox1.Text = reader.ReadToEnd() End Using End If
The code in Listing 2 behaves precisely like the code in Listing 1. Notice that close is not called explicitly but the using block ensures that it is in fact called.
If you are from the show-me state or you're just one of those trusting people who believes in verification, look at the MSIL (use ildasm.exe to view it). Figure 1 shows that the code compiled with the using block inserts a try-finally block into the MSIL for you.
Figure 1: .NET Inserts a Try-Finally Block in the Emitted MSIL Where It Encounters a Using Block
If you employ the using block, you cannot include a catch block or an additional finally block. Think of using-end using as synonymous with the try-finally block. If you want more control (logging exceptions, for example), revert to the try-catch-finally block.
The Choice Is Yours—to a Point
Each programmer has to decide which constructs and idioms he or she wants to use from the set of all language features. However, controlling which features other programmers use is difficult, if not impossible. So, even if you don't want to code the using block, other programmers might use it. For this reason, you will have to recognize it when it is encountered.
Remember the using block is a convenient, shorthand notation for the try-finally block. Using-end using ensures that Dispose is called even when an exception occurs—and that's a good thing.
About the Author
Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Look for his upcoming books UML DeMystified from McGraw-Hill/Osborne (October 2005) and C# Express from Addison Wesley (Spring 2006). Paul is also the founder and chief architect for Software Conceptions, Inc. founded 1990. He is available to help design and build software worldwide. You may contact him for consulting opportunities or technology questions at firstname.lastname@example.org.
If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.
Copyright © 2005 by Paul T. Kimmel. All Rights Reserved.