September 2011 - Posts
Can you pick the problem with this code ? :
Dim currentChar As Char
For i = 0 To largenumber
currentChar = getChar(i)
If currentChar = "a" Then
count += 1
End If
Next
The answer of course is the literal “a” is of type string, not of type Char. Hence currentChar gets implicitly widened to a String to make the expression a string comparison expression. This means a new string is created with the contents of currentChar on each iteration, and the comparison is a string comparison which checks for null strings and empty strings and then does a compare ordinal etc. This is incredibly inefficient.
If “a” was typed as Char, then the comparison is a simple low level IL compare of the underlying values (int16’s).
You can use CChar, as in CChar(“a”), but it’s a lot easier just to add the type literal suffix c, eg “a”c
Dim currentChar As Char
For i = 0 To largenumber
currentChar = getChar(i)
If currentChar = "a"c Then
count += 1
End If
Next The change in this code is about 20 fold performance improvement (run as release build outside of Visual Studio). That’s a 20 fold increase just by typing one extra letter !!
This example was based on real sample code where a massive file was being parsed char by char. There too, attention to the fine detail showed a massive performance improvement over the original code. It’s often the little things that can make a huge difference.
This VB quark was brought to you by the type literal “c”
Do you know why you can’t write this code in VB:
Dim x = 123456789.0123456789
Answer: The IDE won’t let you
If you try to write that code the IDE will truncate the number, giving :
Dim x = 123456789.01234567
To include all the decimal places you need to be using the Decimal type. To do this you indicate the constant is of type decimal by adding the suffix D on the end, eg:
Dim x = 123456789.0123456789D
The reason for this is VB treats numeric literals as of type Double by default if they have a decimal place. If there is no decimal place in the literal then the value is consider to be an Integer (Int32), or if it is too large for an Integer, it’s considered to be a Long (Int64).
If you want the literal as a type other than Double, Integer or Long you need to either convert using CType, CShort, CSng etc, or add a type literal suffix. The following is the complete list for numeric type literal suffixes in VB:
| Suffix | Type |
| F | Single |
| R | Double |
| D | Decimal |
| S | Int16, Short |
| I | Int32, Integer |
| L | Int64, Long |
| US | UInt16, UShort |
| UI | UInt32, UInteger |
| UL | UInt64, ULong |
In case you’re wondering, the “F” for Single is short for "Float”, and the “R” for Double is short for “Real Number”.
Here’s another example of where you should use type literal suffixes. Consider this code :
Dim i1, i2 As Int16
i1 = &HF0
i2 = i1 And &HFF
If you have Option Strict On, the last line will cause a compile time error saying that an implicit conversion from Integer to Short is not allowed. So what is happening here, and why doesn’t the middle line cause the same problem ?
Well the line i1 = &HF0 compiles fine because the constant &HF0 can be evaluated at compile time, so there’s no problem there. The last line however cannot be evaluated because the variable i1 is not a constant. Try it for yourself if you like: change the declaration of i1 to a Const, and you’ll see the last line compiles fine with Strict On.
Because VB can’t evaluate the variable expression at compile time, it uses the default type for numeric constants : Integer (aka Int32). And because a short can be implicitly widened to Int32, the expression on the right hand side becomes an Int32: hence the error.
In Visual Studio the error correction wizard will suggest converting the entire expression to an Int16, as such :
i2 = CShort(i1 And &HFF)
But that’s a runtime conversion that isn’t needed if you declare the &HFF constant with the correct type literal suffix S:
i2 = i1 And &HFFS
As you can see, the code correction wizard in Visual Studio doesn’t always provide the best syntax to use, so it really is up to you to know the correct literals to use and apply them.
Oh, you may have also noticed there is no type literal suffix for Byte and SByte. I’ll leave that for yet another quark 
Can you spot the problem with this code:
<Extension()>
Public Function ToColor(argb As UInteger) As Color
Return Color.FromArgb( _
CType((argb & &HFF000000) >> &H18, Byte), _
CType((argb & &HFF0000) >> &H10, Byte), _
CType((argb & &HFF00) >> 8, Byte), _
CType(argb & &HFF, Byte))
End Function
If you said it should be the bitwise And operator not the string concatenation operator give yourself a pat on the back. The problem here is three fold:
- The code is a failed attempt at translating from C#, and
- The project must have Option Strict Off for this code to even compile.
- No testing or unit tests to check if the code works at runtime.
Sadly this is all too common. The above is in fact a very real example taken from the Silverlight Toolkit samples on Codeplex .
So the steps to correct this are first be aware of the language equivalents when translating from C# to VB. See the MSDN documentation on language equivalents for a great starting point. And take note of the operator equivalents. Some of the most common operators where people seem to make mistakes are:
| operator | VB | C# |
| bitwise And | And | & |
| bitwise Or | Or | | |
| bitwise exclusive Or | XOr | ^ |
| Short circuited Boolean And | AndAlso | && |
| Short circuited Boolean Or | OrElse | || |
| equality | = | == |
| assignment | = | = |
| not equal | <> | != |
| exponent | ^ | |
| modulus | Mod | % |
| string concatenation | & | + |
From the short list above you can see the ^ and & operators have very different meanings in VB compared to C#. Which leads us to items 2 & 3.
If you are unsure of VB, or are translating and hence have a lot of code that *may* be invalid you can improve catching of these kind of developer mistakes by turning Option Strict On. In the original example, the argb & &HFF000000 would have given the result of a string: argb.ToString followed by –16777216. At runtime this string would then try to be converted to a number and then shifted right, most likely failing.
So if you turn Option Strict On, you’d immediately get warnings on trying to convert the string to a long. But even once you’ve got it compiling with Strict On, you should test the code. A lot of code you can quickly query in the immediate window, or write unit tests.
Oh, and the corrected code in this particular case is:
<Extension()>
Public Function ToColor(argb As UInteger) As Color
Return Color.FromArgb( _
CType((argb And &HFF000000) >> 24, Byte), _
CType((argb And &HFF0000) >> 16, Byte), _
CType((argb And &HFF00) >> 8, Byte), _
CType(argb And &HFF, Byte))
End Function
For a complete project that fixes the VB samples for the Silverlight Toolkit, see my blog post from last month
Building upon VB Quark #1 , did you know you can use compiler directives alongside expressions in constants ?
#If CONFIG = "Debug" Then
Const path As String = "Z:\mydebug.sdf"
#Else
Const path As String = "|DataDirectory|\Database1.sdf"
#End If
Const connectionString = "Data Source=" & path
The nice thing about this is as you change the config from Debug to Release, Visual Studio will grey out the parts that aren’t in the current compilation. In the above example, when I do a debug build I’m using my database in Z:\mydebug.sdf, but when I do a release build it automatically uses the database Database1.sdf in the application’s DataDirectory.
You can use compiler directives pretty much anywhere in your code to replace any code block with another at compile time based on the directives.
This quark is more of a did ya know quark about constant expressions in VB. You probably know you can declare a constant expression such :
Const appName As String = "My Really Cool App"
But did you know you can also do operations in constants ?
Rather than calculate the area of a circle you can write:
Const radius = 4.2
Const area = Math.PI * radius ^ 2 The compiler will calculate the area constant at compile time. This makes it easy to change the area constant by simply changing the radius.
The operations you can do include all the standard mathematical operations : +, –, /, \, * , ^ and Mod as well as bitwise operations such as And and Or as well as bit shifting << and >>.
For example you can declare an enum for bits using shift operations: (note: Enums are constants)
Public Enum bits
bit0 = 1
bit1 = 1 << 1
bit2 = 1 << 2
bit3 = bit2 << 1
End Enum
You can also use the If operator. For example you might want to limit a constant based on another constant, eg if gravity is greater than 20g’s then you might want to limit it to 20g for calculations.
Const workingG = If(g < 20 * 9.8, g, 20 * 9.8)
And you can also do conversions between the intrinsic numeric types, eg CDbl, CSng, CDec, CLng, CInt, CShort, CByte, CULng, CUInt, CUShort, CSByte. And conversions to and from boolean to the numeric types: CBool and the aforementioned conversion operators.
Const roughArea As Int32 = CInt(area)
For dates, there aren’t any operations or conversions you can do: you have to define them using the literal representation in US format, eg today 24/9/2011 is defined as #9/24/2011# . This is still a lot better than certain other languages (not picking on C#) that don’t support constant date literals.
For strings, you can’t do any conversions but can use the concatenation operator &. You can use + instead of &, but I don’t recommend that (more on that in another quark). Strings that are compiled as constants are also typically interned.
Anywhere you use an expression that can be compiled as a constant, it is evaluated at compile time: you don’t have to declare a constant variable to reap the benefits of compile time evaluation.
This VB Quark is the one that started the conversation some weeks back. It’s number 0, both because these days things are typically 0 based in .NET, and also you could say it is ground zero. It’s about a quirk quark to do with lambda functions versus lambda subs.
Let’s say you have a Customer class that has an Approved property :
Class Customer
Public Property Approved As Boolean
End Class
Now if you were to write the following code on a list of Customer, what would you expect to happen ?
customers.ForEach(Function(cust) cust.Approved = True)
If you said nothing, you’d be pretty close to the answer.
The quirk quark here is two fold. First of, ForEach expects an Action(Of T) to be passed to it. Action(Of T) in this case has the form of:
Sub DoAction(cust As Customer)
'code here
End Sub
But note we passed in a Function. Function’s have return types. In this case the return type is a Boolean implied from the expression cust .Approved =True . That is the = operator is an equality operation in this case, not an assignment operator. The original expression is seen by the compiler as the equivalent of :
customers.ForEach(Function(cust)
Return cust.Approved = True
End Function)
If you actually want to set the IsApproved values in the ForEach you can simply use a Sub instead of a Function:
customers.ForEach(Sub(cust) cust.Approved = True)
So the quarks to remember here are the = operator in VB can be either equality test or assignment depending on the context; and not all lambdas are lambda expressions (aka Functions), some can be lambda statements (aka Sub)
I’ve seen this quark trip up a couple of really smart people, usually when they are translating code from C# to VB. Apparently some translation tools also make the same mistake.
A few weeks ago I was having a discussion about a particular language feature that was causing a couple of people to trip up in their code. From that discussion we identified some places where the VB compiler and IDE can possibly help in the future; but for today there are some language features that often go unseen but combined do matter (geek pun intended).
So I’ve decided to put together a series of posts I’m calling VB Quarks. The things I’ll be covering are your gotchya’s, the easy to forget things, the little things that combined matter (oh sorry same sad pun again
).
With Windows Phone 7.5 (aka Mango) being released in the next couple of weeks, you may have to work around a google limitation to get multiple google calendars on your device. Both Windows Phone 7.5 and google calendar allow you to have multiple calendars on your phone, but for whatever reason Google doesn’t let you see how to set that unless you trick it to think you’re using an iPad or iPhone. (shame on you google
)
To trick google, you just need to change your browser user agent string. The steps are:
- setup your google account on your phone as normal and sync with calendar and email. This will give you only the one google calendar for that account.
- Next open up IE 9 on your desktop or laptop computer, press F12 to bring up the IE developer tools. Click on the developer tools Tools menu and select Change user agent string –> Custom
Add this user agent string:
Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10
- Next browse to http://m.google.com/sync and log in to your google account. You should now see your windows phone device listed. Click on that and choose which of your google calendars to sync
Kudos to Anthony Chu who posted how to do this with Safari.