| 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 Eggheadcafe.com 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 youbetcha@mindless.com. 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."
|