5 Traps to Avoid in Visual Basic
Visual Basic .NET possesses naturally expressive syntax that is almost always clear in its intent. Even so, there are still surprises in Visual Basic that can trip up even experienced VB developers.
One of the things I truly love about writing code in Visual Basic .NET is its naturally expressive syntax that is almost always clear in its intent. Even so, over the years I have encountered a few surprises while working in Visual Basic that each developer should keep in mind regardless of their experience level with Visual Basic.
For an excellent article on potential issues in C#, see Patrick Steele's recent C# Corner article, "5 Traps to Avoid in C#
Trap #1: Module level variables in ASP.NET web applications
I spent my first years of .NET experience developing desktop Windows Forms applications. Eventually I was asked to architect and develop a major ASP.NET application to provide time card services for an organization of over 600 employees.
As an "upgraded" Visual Basic 6 desktop developer, I was in the habit of defining most of my variables in a Module for use throughout the application, which caused me to fall into Trap #1. Module level variables are shared, and for an ASP.NET Web Forms application, are a single value shared across all users. This is not a problem for truly unique values such as database connection strings, application settings, etc.
Unfortunately. I had made the mistake of defining UserID and other user-specific keys as module variables. In the first multi-user test, we experienced the surprising behavior of other users' time cards randomly appearing on-screen during a data entry session. Moving the user-specific variables out of the Module and into an instance class solved the problem.
Today I always define variables with the narrowest possible scope and use Module or shared variables for only those values that can safely exist as a single value across all users of the application, even when developing desktop applications.
Trap #2: Neglecting to use or incorrectly using short-circuit conditionals
I still see developer code where short-circuit conditionals are not used when possible or are used incorrectly.
If (b = 0) Or (a / b > 2.0) Then
executes both conditions even if b=0 evaluates to true, producing a Divide By Zero exception in the second expression. Rewriting the statement as
If (b = 0) OrElse (a / b > 2.0) Then
will not throw as exception as the second expression will be evaluated only if (b = 0) is false. The same short-circuit behavior is true for the AndAlso alternative to the And operator.
Using the short-circuit conditional operators improves performance by avoiding unnecessary conditional evaluations. Since evaluation is from left to right, the order of appearance of the conditions is significant. The left most condition should be the least expensive expression or one that will help avoid an exception in further evaluation of the statement.
To avoid Trap #2, always use the OrElse and AndAlso operators whenever possible to avoid exceptions and improve performance.
Trap #3: Implied Namespace in VB
In Visual Basic you do not have to be as explicit with your namespace conventions as in C#. Visual Basic will implicitly use the default namespace of the project if a namespace is not defined.
Public Class Ship
Property MaxWeight As Single
Property Capacity As Single
Even though the class is never wrapped in a namespace, it will be given the default namespace of the project in which it resides. The ambiguity ensues when you go to use the class in an external library.
Dim blackPearl As New Vehicles.Ship
The Vehicles namespace is not easily identified from looking solely at the class definition for the Ship class. The only way to determine the namespace is to check the default namespace property for the project in which the Ship class was defined. This namespace ambiguity can be avoided by explicitly marking the namespace of the class.
Public Class Ship
Property MaxWeight As Single
Property Capacity As Single
It is good practice to always be explicit with namespaces for all your classes. Your code will be easier to understand and maintain in the long run. You will also avoid breaking code that externally references your class should you decide to change the default namespace of your library.
To avoid Trap #3, always use explicit namespaces for all your classes.
Trap #4: Leaving Option Strict Off
By default, Visual Basic projects have Option Strict set to Off. To avoid potential runtime type conversion errors, and improve performance by avoiding late binding, set Option Strict to On.
The Option Strict statement restricts implicit data type conversions to only widening conversions. A widening conversion changes a value to a data type that can accommodate any possible value of the original data. Example widening conversions include from integer to long, decimal to double, char to String, etc. Correspondingly, a narrowing conversion changes a value to a data type that may not be able to hold some of the possible values.
A narrowing conversion can still be explicitly performed if Option Strict is On. For example, the first assignment to myInt below will fail since it implicitly converts from a 64-bit long to a 32-bit integer. The second and third assignment will succeed since the conversion is explicit, which places the burden on the developer to ensure only compatible values are used within the application.
Option Strict On
Public Class Class1
Dim myInt As Integer = 10
Dim myLong As Long = 20
Public Sub New()
myLong = myInt
myInt = myLong 'Error Option Strict On disallows implicit conversions from 'Long' to 'Integer'
myInt = CType(myLong, Integer) 'Explicit conversions are still permitted
myInt = CInt(myLong) 'Explicit conversions are still permitted
Late binding is runtime type resolution. It can be essential in some situations, such as working with COM objects, but in general it requires more work for the compiler and should be avoided.
To avoid Trap #4, set Option Strict On to be the default for Visual Basic projects. Go to Tools, Options, Projects and Solutions, VB Defaults and set the Option Strict default to On as shown in Figure 1.
[Click on image for larger view.]
|Figure 1. Set the default for Option Strict to On.
Trap #5: Consuming Errors
Error handling is provided in Visual Basic via the Try... Catch... Finally structure. By default, the Catch block is empty and will "eat" any error and fail to present an error message to the user or to the application.
Whether by neglect or intent, I sometimes see code with empty Catch blocks similar to the following sample. The invalid filename will fail to present any error and the code will inconveniently fail at some later point, away from the original cause of the issue.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Dim FileName As String = "Intentionally Invalid FileName.txt"
Dim TextLines() As String = System.IO.File.ReadAllLines(FileName)
Catch ex As Exception
If you can anticipate the types of errors that may occur in your method, such as the System.IO.FileNotFoundException that would have been thrown here, they should be handled explicitly, in the order of most likely occurrence.
My preferred statement to handle unanticipated errors is similar to the line below. It provides the opportunity for a user-friendly error message, identifies the location of the error in the code, and preserves the original exception as the inner exception. Often users will report only the first line of the error message and in this way the developer has sufficient information in that first line to locate and often quickly resolve the error.
Throw New Exception("File Not Found. Form1.Button1_Click Error: " & ex.Message, ex)
To avoid Trap #5, always include appropriate error handling code in each and every Catch block.
I'd like to Eric Vogel for his contributions on Trap #3, "Implied Namespace in VB". Avoiding these traps requires little effort and will significantly improve the quality of your code. Good luck and happy computing!
Joe Kunk is a Microsoft MVP in Visual Basic, three-time president of the Greater Lansing User Group for .NET, and developer for Dart Container Corporation of Mason, Michigan. He's been developing software for over 30 years and has worked in the education, government, financial and manufacturing industries. Kunk's co-authored the book "Professional DevExpress ASP.NET Controls" (Wrox Programmer to Programmer, 2009). He can be reached via email at email@example.com.