Cool .NET Tips and Tricks #8: The VB.NET Debacle
By Dr. Dexter Dotnetsky
Printer - Friendly Version
Dr. Dotnetsky

The VB.NET / C# debacle rages on. . .

Well, its Dr. Dotnetsky again. They let me out of my cage for a while, and this time I'm going to take a little detour and make some observations about developers targeting different .NET languages, specifically VB.NET and C#.

It's always been good programming practice to use the most specfic type possible; in C++ and C# this is the norm. It would make no sense for a C# programmer to write:

object x;

when they really want:

int x;

even though the first one is perfectly acceptable.

In Visual Basic 6 and earlier, we had a lot of "lazy" flexibility with variable types. The VARIANT type would allow us to stick just about anything in it and we even had the option of not declaring variables at all. Lazy typing created a lot of - you guessed it -- lazy programmers. It also created inefficiencies and a much higher probability of errors in code. Many VB programmers migrating to the .NET platform want to keep their old bad habits, not the least of which is never setting Option Explicit on. This allows them to write:

Dim x

and happily go about their programming duties (of course in VBScript, which became ubiquitous because of Classic ASP under IIS, that's your only choice!). Unfortunately, Microsoft has perpetuated this illogical paradigm: because they have an estimated 4 million "VB" programmers on whom they've successfully foisted this weakly typed paradigm, and because they wanted to provide an easy migration path to the .NET platform with VB.NET (which is a full-fledged member of the CLR-compliant programming languages - but ONLY if you use it correctly) they wrote a whole slew of backwards-compliant classes (most of which can be found in the Microsoft.VisualBasic namespace) to enable developers to continue using contructs like Mid(), CType(), and others. None of this stuff is Base Class Framework or ECMA CLI compatible, thank you! And of course, human nature being what it is, millions of VB programmers are continuing on their merry DIM way, happily abusing the CLR type system by almost exclusively using these built-in crutches.

Option Strict is not DIM?

Option Strict is the newest addition to VB.NET -- Option Explicit forces us to actually declare all our variables (God forbid!), and Option Strict forces us to type them. In VB 6.0, if you declared "Dim x", you got a Variant, which incurred a performance penalty. However in VB.NET, if you choose to do this you will incur a very BIG performance penalty! You are going to get a System.Object instance, which is a reference, not a value type, and that means boxing.

If we attempt to add two undeclared variables with Option Strict turned off, the compiler generated IL contains boxing code, moving the value types into the heap by sticking them into a referenced type. Boxing is performed with System.Object, and there is a performance hit because an additional object has to be maintained for each undeclared variable. In addition, there will be a call to the Microsoft.Visualbasic compatibility layer:

IL_0015: call object [Microsoft.VisualBasic]
Microsoft.VisualBasic.CompilerServices.ObjectType::AddObj(object, object)

There are cases when an instance of a value type needs to be treated as an instance of a reference type. For situations like this, a value type instance can be converted into a reference type instance through a process called boxing. When a value type instance is boxed, storage is allocated on the heap and the instance's value is copied into that space. A reference to this storage is placed on the stack. The boxed value is an object, a reference type that contains the contents of the value type instance. A boxed value type instance can also be converted back to its original form, a process called unboxing.

Understanding the Difference

The key to this issue is understanding the difference between value types and reference types, which is a fundamental distinction in the Common Type System, and this means having an understanding of how memory is allocated for instances of each type. In managed code, values can have their memory allocated either on the stack or on the heap, both of which are managed by the CLR. Variables allocated on the stack are normally created when a running method creates them. From a memory perspective, the basic difference between value types and reference types is that an instance of a value type has its value allocated on the stack, while an instance of a reference type has only a reference to its actual value allocated on the stack. The value itself is allocated on the heap.

The memory used by stack variables is automatically freed when the method in which they were created returns. However, variables allocated on the heap don't have their memory freed when the method that created them returns. Instead, the memory used by these variables is freed via the garbage collection process, which can (and often does) occur whenever it "feels like it".

It is this second process, involving calling the Microsoft.VisualBasic namespace, creating and boxing the new variables (the ones you didn't declare and type) and finally storing references to these values on the stack and the actual boxed values themselves on the heap is what happens when you don't declare and type variables in VB.NET.

The bottom line is there is a definite performance penalty for undeclared or untyped variables in VB.NET. With Option Explicit On and Option Strict On, we must declare our two variables and type them before attempting our addition. The two variables in the compiler - generated IL are declared on the Stack with no boxing, which is obviously less lines of IL code as well as being much more efficient code.

Unfortunately, in bringing out the final release of Visual Studio.NET, Microsoft bowed to the cries of millions of VB programmers and left Option Strict Off by default in all our VB.NET projects! Just about every professional author and programmer will tell you that you should ALWAYS set Option Strict to "On"!

Forcing good programming habits with the IDE

Fortunately, there is a very easy way to do this so that you won't forget. You are hereby strongly encouraged to go to the Visual Studio's Tools | Options menu, click the Projects \ VB Defaults folder and set Option Strict and Option Explicit to On. You might then spend a bit more time writing CType-s or DirectCast-s, but you will avoid spending much more time tracking down some mysterious runtime errors. Many of your programs will also run FASTER.

Now all your projects will automatically have "Option Explicit" and "Option Strict" turned on. With C#, you don't have this problem - it has much stricter type safety requirements. If you declare a variable as an object and you really want to use it as an int, you must explicitly cast it to an int (e.g. (int) x ) before you can use it in an arithmetic computation. This process provides the compiler enough information to generate more efficient code (but not as efficient as if you had declared it as int in the first place).

There are other issues you should consider if you are a VB.NET programmer (besides the option of learning C#, which I highly recommend - after all, ASP.NET contains over a million lines of code, and they were all written in C#, so who is kidding whom?)

Instead of using CType() and similar cast keywords on reference types, use DirectCast(). Instead of using Mid() with strings, consider using the StringBuilder methods. String.Concat() and its overloads are also much faster than using the "&" operator in VB.NET. If you disassemble your VB.NET assemblies with ILDASM and you see a lot of references with the word VIsualBasic in them, then trust me -- you have successfully created inefficient code! To Dr. Dotnetsky, this is the equvalent of drinking Burgundy when you have the money to buy Margaux. Here is an example:

IL_0048: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.LateBinding::LateGet(object, class [mscorlib]System.Type, string, object[], string[], bool[])
IL_004d: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
IL_0052: call void [mscorlib]System.Console::WriteLine(object)
IL_0057: ret

As a last point of mention, for those who are having difficulty translating C# code to VB.NET and vice-versa, I would recommend Lutz Roeder's fine Reflector product. Compile your stuff in C#, load the assembly into Reflector, choose VB as the language, and hit the Decompiler choice with a method highlighted. Presto - instant "other language" in the code window! You'll also learn a lot about the CLR with its other features. Highly recommended! Price, free.

Finally, if you really want to excel in creating efficient, compact managed code, don't use either C# or VB.NET - -use C++. That's my rant for this week!

Dr. Dexter Dotnetsky is the alter-ego of the forums, where he often pitches in to help answer particularly difficult questions and make snide comments. Dr. Dotnetsky holds no certifications, and does not have a resume. Always the consummate gentleman, Dr. Dotnetsky can be reached at  Dr. Dotnetsky's motto: "If we were all meant to get along, there would be no people who wait for all the groceries to be rung up before starting to look for their damn checkbook."