Code Focused

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.

The statement

        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
End Class

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.

Namespace Vehicles
Public Class Ship
Property MaxWeight As Single
Property Capacity As Single
End Class
End Namespace

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
End Sub
End Class

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) _
Handles Button1.Click
Dim FileName As String = "Intentionally Invalid FileName.txt"
Try
Dim TextLines() As String = System.IO.File.ReadAllLines(FileName)
Catch ex As Exception
End Try
End Sub

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.

Final Thoughts
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!

About the Author

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 joekunk@ajboggs.com.

comments powered by Disqus

Reader Comments:

Tue, Feb 14, 2012 Australia

I use On Error in VB.net everywhere I can - which is almost everywhere. It does the job and allows me to use a centralised error logger much more easily than with Try Catch. Also, have a single On Error wrap the entire procedure results in less spagetti-like code that having 3 or 4 Try-Catch blocks. Also, I cannot live without On Error Resume Next.

Wed, Aug 17, 2011 Richard

@Scott: Because it's horrible! :)

"On Error Resume Next" wraps *every single statement* in a "Try (the statement) Catch (ignore it)" block. It was a bad idea in VB6. It was a bad choice in VBScript, even though you didn't have another option. It's certainly a bad idea in VB.NET!

"On Error Goto [Label]" results in a mess of spaghetti-code which is a pain to read. You might as well go back to QBASIC and use GoSub and Return everywhere!

There's nothing sensible you can write using the VB6-style "On Error" code that can't be better written using Try/Catch/Finally blocks.

Fri, Aug 12, 2011 Scott Nation Minneapolis, MN

Richard, why should the VB6-style "On Error" approach be avoided at all costs? It does everything I need it to do.

Fri, Aug 12, 2011 Richard

@Douglas: Wrapping the conditions in parentheses doesn't resolve the problem; the "Or" operator still attempts to evaluate both conditions, which results in an exception.

Microsoft originally intended to make "And" and "Or" short-circuiting in VB.NET, but it broke too many badly written VB6 applications which relied on side-effects from evaluating the conditions.

The other major disparity in the name of backwards-compatibility is with array declarations. In almost every other language, you declare the number of elements in the array; in VB, you declare the upper bound of the array. Again, Microsoft were planning to change this for VB.NET, but it broke too much VB6 code.

I think Trap #5 should be expanded to mention the VB6-style "On Error" approach, which should be avoided at all costs.

Tue, Mar 1, 2011 Marian Adamjak Slovakia

Nice, article. I went through the same traps, I know it. But It is very good idea to reminde them. Thanks a lot.

Thu, Feb 24, 2011 Richard UK

I have few months of experience only in programming, however I don't understand why late binding is a problem. I use it constantly to get values to populate listboxes, menus and other items in a simple winforms app. Putting option strict on breaks this functionality, leaving me only the option of having to know explicitly the data making up the menus. What is the "proper" answer according to tip 4? The app is a tool used to work on database content.

Fri, Feb 18, 2011 Marcus Iowa

There's a caveat on #3. In VB the Namespace statement ADDS TO the Default Namespace. If I do as you suggest here and put Namespace Bob around Class1 in project MyTools, the resulting class is MyTools.Bob.Class1, not Bob.Class1. You can edit your project and remove the Default Namespace, but then you MUST set Namespace on every class or they'll end up in the Global namespace (e.g. Global.MyNoNamespaceClass).

Thu, Feb 17, 2011 SAMEER LAL CA

Great tips!! @Gunnar #5 is on page 2.

Thu, Dec 2, 2010 Russell Mazonde Harare, Zimbabwe

Thanks for the tips, esp 1 2 5. Very helpful

Wed, Dec 1, 2010 Douglas Phillips Illinois

Regarding Tip #2 (short-circuit conditionals), this is exactly why I loath Visual Basic. Anyone coming from a C/C#/Java background would write it using the "Or" keyword, assuming that it would work as expected in comparison to "||". This type of thing (Microsoft and their odd-ball operators just for the heck of it) makes code maintenance hell when you're maintaining multiple languages. (In my case, I maintain code in C#, Java, and VB, in addition to a host of scripting languages. I hate having to stop and make sure that Microsoft didn't decide to put some new extra keyword in the VB language just because...) As an aside, this problem is also resolved by the programmer wrapping the condition in parenthesis (which is what I would have done in the first place to make sure that it's evaluated properly): If ((b=0) Or (a/b > 2.0))... -Doug

Wed, Nov 17, 2010 Clarke Anderson East Lansing, Michigan

(See page two for the 5th tip!)

Wed, Nov 17, 2010 Gunnar Stockholm, Sweden

Good tips, but aren't we missing one? That's only 4 traps...

Wed, Nov 17, 2010 Waleed El-Badry Cairo, Egypt

Thanks Joe for those valuable tips. You have just demonstrated a great example of using short circuit condition. I do agree with you that option strict should always be set to on as narrowing conversion and type casting sometimes cause unexpected behavior that could be hardly found during debugging. Great article Joe.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.