To Val or not to Val, That is the QuestionIs the Val function a Great Shortcut or a Secondrate Hack? | | I work with two really great guys that happen to have quite different programming philosophy's. On the one hand, one of them is a recent college grad from one of the nations most prestigious computer science programs. On the other hand is a seasoned vet, who's been programming since before I was alive, who's been using Visual Basic since version 3 and who's published a really great book on the subject. Our department, like most others, has its share of 'what's the best way' arguments. I happen to fall somewhere in between. I've been programming for 6 years, the first of which was using C++ and Oracle OLAP systems. I started using VB with version 6 and was one of the early adopters of the whole '.NET' thing.
So, while I was still learning .NET, I used a lot of old school VB6 functions, amongst which was VAL. We had a few other debates like MsgBox vs. MessageBox.Show (which is the subject of my next article) but this one is worth writing about.
Well, should you use VAL or avoid it? On the one hand, it's an easy way to strip erroneous characters from a string that contains a number you are concerned with. On the other hand, it's a kluge that causes stack overflows and violates everything good in the world. So what's the right answer?
I decided to take it to IL and let it decide. So I wrote the following code snippet (and I had to turn Option Strict Off to get it to run - and if that isn't foreshadowing I don't know what is)...here's the snippet:
<BewareOfImplicitTypeConversionAttribute>_
Public Sub ShowVal()
Debug.WriteLine(Val("10000a"))
Debug.WriteLine(Val("a"))
Debug.WriteLine(Val(1000))
Debug.WriteLine(Val(CType("1000", Int16)))
Debug.WriteLine(Val(CType("1000", Integer)))
Debug.WriteLine(Val(CType("1000", Long)))
Dim i As Integer = Val("10000")
End Sub
If we used the .Parse function, we'd be have an Exception extravaganza... but what does the IL look like:
.method public instance void ShowVal() cil managed
{
// Code size 181 (0xb5)
.maxstack 1
.locals init ([0] int32 i)
IL_0000: nop
IL_0001: ldstr "10000a"
IL_0006: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(string)
IL_000b: box [mscorlib]System.Double
IL_0010: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_0015: nop
IL_0016: ldstr "a"
IL_001b: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(string)
IL_0020: box [mscorlib]System.Double
IL_0025: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_002a: nop
IL_002b: ldc.i4 0x3e8
IL_0030: box [mscorlib]System.Int32
IL_0035: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(object)
IL_003a: box [mscorlib]System.Double
IL_003f: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_0044: nop
IL_0045: ldstr "1000"
IL_004a: call int16 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ShortType::FromString(string)
IL_004f: box [mscorlib]System.Int16
IL_0054: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(object)
IL_0059: box [mscorlib]System.Double
IL_005e: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_0063: nop
IL_0064: ldstr "1000"
IL_0069: call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.IntegerType::FromString(string)
IL_006e: box [mscorlib]System.Int32
IL_0073: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(object)
IL_0078: box [mscorlib]System.Double
IL_007d: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_0082: nop
IL_0083: ldstr "1000"
IL_0088: call int64 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.LongType::FromString(string)
IL_008d: box [mscorlib]System.Int64
IL_0092: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(object)
IL_0097: box [mscorlib]System.Double
IL_009c: call void [System]System.Diagnostics.Debug::WriteLine(object)
IL_00a1: nop
IL_00a2: ldstr "10000"
IL_00a7: call float64 [Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Val(string)
IL_00ac: call float64 [mscorlib]System.Math::Round(float64)
IL_00b1: conv.ovf.i4
IL_00b2: stloc.0
IL_00b3: nop
IL_00b4: ret
} // end of method Form1::ShowVal |
Notice what happens at each line.. everything is converted... So, while you think Val is returning an INT, it isn't. But if you are using VB.NET (the only language that supports VAL) and have Option Strict turned OFF (which should be a crime), you'd never no. .NET can implicitly convert a float64 primitive type to an Int. Actually, the whole power of VAL is precipitated upon the fact that you use implicit type conversion.....and that speaks volumes.
Anyway, I really like and respect both of the guys in this argument, but one argument holds more water. I'm going to let the IL tip you off to who's 'right' on this one.... |