Thursday, November 27, 2008

Program your own ToString method with VB.NET

Every built-in type in VB.NET environment has a ToString method which returns a textual representation of the value. ToString method is declared to System.Object as

Public Overridable Function ToString() As String

and very class that inherits from System.Object inherits ToString method too. Since ToString method is declared as Overridable, inherited classes typically override this base method. Besides inheriting ToString method it is also overloaded with method that accepts a format string as a parameter. More information about format strings for ToString method can be found in MSDN. Here's a link to Int64.ToString method with format string. The same MSDN page contains also examples for formatting numbers, dates and time. Examples are provided to both VB.NET and C#.

When you write your own classes in VB.NET, there's nothing to prevent that you write your own ToString method too. Here's a simple PersonName class that implements ToString method.

Option Explicit On
Option Strict On
Public Class PersonName

  Private _FirstName As String
  Private _LastName As String

  Public Sub New()
    ' Initialize class
    _FirstName = ""
    _LastName = ""

  End Sub

  Public Sub New(ByVal FirstName As String, ByVal LastName As String)
    ' Initialize class
    _FirstName = FirstName
    _LastName = LastName

  End Sub

  Public Property FirstName() As String
    '
    Get
      Return _FirstName
    End Get
    Set(ByVal value As String)
      _FirstName = value
    End Set

  End Property

  Public Property LastName() As String
    Get
      Return _LastName
    End Get
    Set(ByVal value As String)
      _LastName = value
    End Set

  End Property

  Public Overrides Function ToString() As String
    ' Return name as a string
    Dim TempStr As String

    TempStr = ""
    If _FirstName.Length > 0 Then
      TempStr = _FirstName
    End If
    If _LastName.Length > 0 Then
      If TempStr.Length > 0 Then
        ' Add space between names
        TempStr = TempStr & " " & _LastName
      Else
        TempStr = _LastName
      End If
    End If
    Return TempStr

  End Function

  Public Overloads Function ToString(ByVal Format As String) As String
    ' Return name as a string
    ' Format="f", "l", "fl", "lf", "f,l", "l,f"
    Dim TempStr As String

    TempStr = ""
    Select Case Format
      Case "f"
        TempStr = _FirstName
      Case "l"
        TempStr = _LastName
      Case "fl"
        If _FirstName.Length > 0 Then
          TempStr = _FirstName
        End If
        If _LastName.Length > 0 Then
          If TempStr.Length > 0 Then
            ' Add space between names
            TempStr = TempStr & " " & _LastName
          Else
            TempStr = _LastName
          End If
        End If
      Case "lf"
        If _LastName.Length > 0 Then
          TempStr = _LastName
        End If
        If _FirstName.Length > 0 Then
          If TempStr.Length > 0 Then
            ' Add space between names
            TempStr = TempStr & " " & _FirstName
          Else
            TempStr = _FirstName
          End If
        End If
      Case "f,l"
        If _FirstName.Length > 0 Then
          TempStr = _FirstName
        End If
        If _LastName.Length > 0 Then
          If TempStr.Length > 0 Then
            ' Add space between names
            TempStr = TempStr & ", " & _LastName
          Else
            TempStr = _LastName
          End If
        End If
      Case "l,f"
        If _LastName.Length > 0 Then
          TempStr = _LastName
        End If
        If _FirstName.Length > 0 Then
          If TempStr.Length > 0 Then
            ' Add space between names
            TempStr = TempStr & ", " & _FirstName
          Else
            TempStr = _FirstName
          End If
        End If
    End Select
    Return TempStr

  End Function

End Class

The first ToString method has to be declared as Overrides, since the class is inherited from System.Object and the method overrides the method from the base class.

The second ToString method accepts a format string argument and it has to be declared as Overloads, since it overloads our first method. Accepted format strings are "f", "l", "fl", "lf", "f,l" and "l,f" and they affect if either first or the last name is outputted first and how they are separated.

The following example shows, how to test ToString methods and how the output from the our custom ToString method is formatted.

Dim aPersonName As New PersonName("John", "Doe")
MessageBox.Show(aPersonName.ToString, _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("f"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("l"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("fl"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("lf"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("f,l"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)
MessageBox.Show(aPersonName.ToString("l,f"), _
  "Name", MessageBoxButtons.OK, MessageBoxIcon.Information)

And the resulting output is

John Doe
John
Doe
John Doe
Doe John
John, Doe
Doe, John

Thursday, November 13, 2008

Convert CSharp source code to VB.NET source code

Occasionally you search for code samples or code snippets for a specific problem with the search engines. Usually you do find a code snippet but it is written in a "wrong" language, most notably with C#. Then you face the problem, how to convert C# code to VB.NET code. Of course, you can do it manually if you're CSharp literate.

Fortunately there are a few options to translate C# source code to VB.NET automatically and for free. Translators can be divided in two categories, web-based translators and applications that are capable to do the conversion. The pros of the web-based translators are obvious, you don't need to install any additional applications to your computer.

There are a few things to remember when using .NET code translators. Although the original source code might be fully tested, you need to re-test the translated code. There's always some code which can't be translated, at least correctly.

Here's a few rules of thumb to get most of the code converters. Do not try to translate a whole application. The result may be hard to test and the resulted source code may be more or less spaghetti style code. Keep it simple, translate only code snippets or one class at a time.

Finally, code translators usually work in both ways i.e. they translate from CSharp to VB.NET as well as from VB.NET to CSharp.

Web-based CSharp to VB.NET converters

http://www.carlosag.net/Tools/CodeTranslator/ is an on-line translator by Carlos Mares. Supported translations are C# -> VB.NET and VB.NET -> C#. As usually, the code is pasted in the text box and then you press Go-button. The translated code is replaced in the text box. Extra option is to upload a whole file to be translated.

http://www.developerfusion.com/tools/convert/csharp-to-vb/ is an on-line translator by Developer Fusion Ltd. Supported translations are C# -> VB.NET and VB.NET -> C#. Also .NET 3.5 syntax is supported. Extra feature is automatically copy result to clipboard. Developer Fusion's translator gives accurate information if the original source has error. It also gives information about the code parts that are not supported in the target language and thus are not possible to translate.

http://converter.telerik.com/ is an on-line translator by Telerik. Supported translations are C# -> VB.NET and VB.NET -> C#. Like Developer Fusion's translator, this translator gives accurate information if the original source has error. Translator also gives information about the code parts that are not supported in the target language and thus are not possible to translate.

A good list of translators, both free and commercial, can be found on Converting code between .NET programming languages
Converters mentioned above are just samples, new converts seem to arise in the net almost daily.

CSharp to VB.NET converter applications

SharpDevelop (http://www.sharpdevelop.net/) is actually an IDE for .NET programming. In the Tools-menu you'll find "Covert code to"-option. Supported translations are C# -> VB.NET, VB.NET -> C# and a conversions to a bit exotic Boo-language. As you can expect, you'll get messages from syntax errors in the original code. Also code parts that are not supported in the target language are marked with comments. Current SharpDevelop version is 2.2, but version 3.0 is in the beta phase and it will propably support .NET 3.5 syntax.

.NET Reflector (http://www.red-gate.com/products/reflector/) is a tool to view, navigate, and search through, the class hierarchies of .NET assemblies. .NET Reflector was originally programmed by Lutz Roeder but Red Gate Software Ltd. acquired it this year. They still offer a free version of it. Since Reflector handles assemblies rather than source code, it supports quite wide range of conversions. Easiest way to convert from the assembly to source code, is to use a suitable plug-in for the Reflector. In the case of VB.NET conversion, Denis Bauer has a great plug-in for this www.denisbauer.com/NETTools/FileDisassembler.aspx. Supported conversions i.e. source languages generated with this plug-in are C#, Visual Basic and Delphi. Latest version is 5.0.42.0 and it was published in 2007 so there's no .NET 3.5 support.

Which CSharp to VB.NET source code converter to choose from?

My personal favorite is Developer Fusion's translator since I convert often and small C# snippets to VB.NET. I've always got the job done with it and it's fast to use. However, take a look at the other converters too. You may find a more suitable for your needs.

Thursday, November 6, 2008

A quick tip: Check with VB.NET if operating system is Windows Vista

One day I fixed some old VB.NET application that works fine in Windows XP and Windows 2000. The problem was, it didn't work as expected with Windows Vista.

I located the spot which handled Windows registry in a way that did not work in Vista and I was quickly able to write a Vista-compatible version. Since it was not possible to have two versions of the application and I did not have time to re-write that part of the code to be compatible with all Windows versions, I decided to include both code snippets in the same version and just check operating system.

Checking operating system version with VB.NET is an easy job and I wrote a little wrapper for my Windows Vista check:

''' <summary>
''' Returns true if the OS version is any Vista version
''' </summary>
''' <returns>True if Vista OS</returns>
''' <remarks></remarks>
Public Function IsVista() As Boolean
  '
  If Environment.OSVersion.Version.Major = 6 Then
    Return True
  Else
    Return False
  End If

End Function

In the application I use it in the following way:

If IsVista() Then
  ' Vista specific code
Else
  ' Code for older Windows versions
End If

That saved me a lot of time and testing.

Friday, October 3, 2008

Check with VB.NET if running under IDE

When you write your VB.NET application and debug it under IDE, you know it's running under IDE. Your application does not know it but in some cases it should know that it's running under IDE.

When the application can check if it's running under IDE or as a standalone application, it can make different decisions based on that knowledge.

One scenario would be to use different paths to data files. Under IDE the application can use path to some data made for debugging purpose. When running as a standalone application it can use path to real data.

Another scenario where your application can use this information, is to display debugging information. When you debug application under IDE, it can use extensive debug information dumping. But when you hand your application over to testers, they like to have your application behaving like end users would see it.

Test with VB.NET if running in IDE

With VB.NET it's quite simple to test if the application runs in IDE. System.Diagnostics namespace contains Debugger class. From Debugger class you can check IsAttached property which tells if a debugger is attached to the running process. This is the case when your application runs in IDE.

''' <summary>
''' Returns boolean value telling if the application is running under IDE
''' </summary>
''' <returns>True if the application is running under IDE</returns>
''' <remarks></remarks>
Public Function RunningUnderIDE() As Boolean
  '
    Return System.Diagnostics.Debugger.IsAttached()

End Function

Test with Visual Basic 6 if running under IDE

Just for the comparison I went through my "Old Code Archive" and under the dust I found how the same test was done with VB6. I have used this code to test if my application was running in IDE with VB6, but I know there were other (maybe simpler) ways to do it.

First, some helper routines and declarations.

Const FileFromFullName = 2

Private Declare Function GetModuleFileName Lib "kernel32" _
  Alias "GetModuleFileNameA" (ByVal hModule As Long, _
  ByVal lpFileName As String, ByVal nSize As Long) As Long
  
Private Declare Function GetModuleHandle Lib "kernel32" Alias _
  "GetModuleHandleA" (ByVal lpModuleName As String) As Long

Private Function GetFileFromFullName(ByVal SourceFile As String) As String
'
' Return the file name and extension part of SourceFile.
'
Dim SlashPos As Integer
Dim LastPos As Integer
  
  ' Find out the position index for the last slash character (LastPos)
  SlashPos = InStr(SourceFile, "\")
  LastPos = SlashPos
  Do Until SlashPos = 0
    SlashPos = InStr(LastPos + 1, SourceFile, "\")
    If SlashPos <> 0 Then
      LastPos = SlashPos
    End If
  Loop
  
  ' Now return last 'LastPos' chars from the original SourceFile string
  GetFileFromFullName = Mid$(SourceFile, LastPos + 1)

End Function

Public Function GetProcessName() As String
Attribute GetProcessName.VB_Description = "Returns the name of the mother process, which is different to app.name if program is a dll."
'
' Returns the name of the mother process (different to app
' if we are in a dll)
'
Dim StringBuffer As String
Dim FileName As String
Dim Length As Long
  
  StringBuffer = Space(255)
  Length = GetModuleFileName(GetModuleHandle(vbNullString), _
    StringBuffer, Len(StringBuffer))
  FileName = GetFileFromFullName(Left$(StringBuffer, Length))
  FileName = Left$(FileName, Len(FileName) - 4) ' Remove .exe
  GetProcessName = FileName

End Function

And finally the actual function.

Public Function RunningUnderIDE() As Boolean
Attribute RunningUnderIDE.VB_Description = "Returns boolean value telling if program is running under IDE. False means that program is compiled version."
'
' Returns boolean value telling if we are running under IDE
' (False means we are running the compiled version)
'
  RunningUnderIDE = (GetProcessName <> App.EXEName)

End Function

Well, it worked but that's a quite lot more code than in VB.NET version.

Monday, September 29, 2008

Get disk drive type with VB.NET

Sometimes you have to find disk drives of particular type. For example, you may have to search all removable disk drives or network mapped drives with VB.NET.

This sample shows how to check all drives and get their drive type, volume label and check if the drive is ready.

First, create a new standard WinForms project. Drop in a one button control and a one listbox control.

Import System.IO namespace

Imports System.IO

and then add the following declaration and helper procedure after form's declaration

Public Declare Function WNetGetConnection Lib "mpr.dll" Alias "WNetGetConnectionA" _
 (ByVal lpszLocalName As String, ByVal lpszRemoteName As String, ByRef cbRemoteName As Integer) As Integer

Public Function GetNetDriveName(ByVal DriveLetter As String) As String
  '
  ' Return mapped drive UNC name
  Dim RetVal As Integer
  Dim OutName As String = New String(CChar(" "), 260)
  Dim NameLength As Integer = 260

  Try
    RetVal = WNetGetConnection(DriveLetter, OutName, NameLength)
    OutName = OutName.Replace(Chr(0), " ").TrimEnd(CChar(" "))
    Return OutName
  Catch ex As Exception
    Return ""
  End Try

End Function

Mpr.dll is a Windows module which handles communication with installed networked providers. In this case its used in the helper function above to get UNC-name for mapped network drives.

Next is the actual procedure which loops drive letters and returns available information.

Public Sub GetDrives(ByRef DriveLetter() As String, ByRef VolumeLabel() As String, _
  ByRef DriveTypeVal() As DriveType, ByRef PathToDrive() As String, _
  ByRef IsDriveReady() As Boolean)
  '
  ' Return available disc drives
  '
  Const DRIVELETTERS As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  Dim Info As DriveInfo
  Dim Count As Integer
  Dim i As Integer

  Try
    Count = DRIVELETTERS.Length - 1
    ReDim DriveLetter(Count)
    ReDim VolumeLabel(Count)
    ReDim DriveTypeVal(Count)
    ReDim PathToDrive(Count)
    ReDim IsDriveReady(Count)
    For i = 0 To Count
      DriveLetter(i) = DRIVELETTERS.Substring(i, 1)
      VolumeLabel(i) = ""
      DriveTypeVal(i) = DriveType.Unknown
      PathToDrive(i) = ""
      IsDriveReady(i) = False
    Next i
    For Each Info In My.Computer.FileSystem.Drives
      Count = DRIVELETTERS.IndexOf(Info.RootDirectory.FullName.Substring(0, 1))
      DriveTypeVal(Count) = Info.DriveType
      If Info.IsReady Then
        IsDriveReady(Count) = True
        VolumeLabel(Count) = Info.VolumeLabel
      Else
        IsDriveReady(Count) = False
        VolumeLabel(Count) = ""
      End If
      If Info.DriveType = DriveType.Network Then
        PathToDrive(Count) = GetNetDriveName(DriveLetter(Count) & ":")
      Else
        PathToDrive(Count) = DriveLetter(Count) & ":"
      End If
    Next Info
  Catch ex As Exception
    ' Error handling
  End Try

End Sub

And finally here's the code for Button1. The code call GetGrives-procedure and displays returned information in the ListBox1.

Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  '
  ' Get drives and their types
  Dim DriveLetter() As String
  Dim VolumeLabel() As String
  Dim DriveTypeVal() As DriveType
  Dim PathToDrive() As String
  Dim IsDriveReady() As Boolean
  Dim TempStr As String
  Dim i As Integer

  Try
    ReDim DriveLetter(0)
    ReDim VolumeLabel(0)
    ReDim DriveTypeVal(0)
    ReDim PathToDrive(0)
    ReDim IsDriveReady(0)
    GetDrives(DriveLetter, VolumeLabel, DriveTypeVal, PathToDrive, IsDriveReady)
    ListBox1.Items.Clear()
    TempStr = "DriveLetter" & "; "
    TempStr = TempStr & "DriveType" & "; "
    TempStr = TempStr & "VolumeLabel" & "; "
    TempStr = TempStr & "PathToDrive" & "; "
    TempStr = TempStr & "IsDriveReady"
    ListBox1.Items.Add(TempStr)
    For i = 0 To DriveLetter.GetUpperBound(0)
      TempStr = DriveLetter(i) & "; "
      TempStr = TempStr & DriveTypeVal(i).ToString & "; "
      TempStr = TempStr & VolumeLabel(i) & "; "
      TempStr = TempStr & PathToDrive(i) & "; "
      TempStr = TempStr & IsDriveReady(i).ToString
      ListBox1.Items.Add(TempStr)
    Next i
  Catch ex As Exception
    ' Error handling
  End Try

End Sub

The output looks something like this:

DriveLetter; DriveType; VolumeLabel; PathToDrive; IsDriveReady
A; Removable; ; A:; False
B; Unknown; ; ; False
C; Fixed; ; C:; True
D; Fixed; New Volume; D:; True
E; CDRom; ; E:; False
F; Fixed; HD-HCU2; F:; True
G; Fixed; HD-HSU2; G:; True
H; Fixed; MAXTOR; H:; True
I; Removable; PORTABLEAPP; I:; True
J; Unknown; ; ; False
K; Unknown; ; ; False
L; Unknown; ; ; False
M; Unknown; ; ; False
N; Unknown; ; ; False
O; Unknown; ; ; False
P; Unknown; ; ; False
Q; Unknown; ; ; False
R; Unknown; ; ; False
S; Unknown; ; ; False
T; Unknown; ; ; False
U; Unknown; ; ; False
V; Unknown; ; ; False
W; Unknown; ; ; False
X; Unknown; ; ; False
Y; Unknown; ; ; False
Z; Network; Vista; \\Cameron\Public; True

The listing shows that drive A is a removable disk but its not ready (it's a floppy disk drive). Drives C, D, F, G and H are hard disk drives and drive C has no volume label. Drive E is a CD/DVD drive and drive I is also a removable drive (USB memory stick). And finally drive Z is a network mapped drive.

What can you do and do not with this information? First, the only USB drive is drive I because drive letters A and B are assigned to floppy disks even if you do not have one. Second, drive Z's volume label is "Vista" in the PC from which it is shared. The share name is \\Cameron\Public so the server's name is "Cameron" and the shared folder is "Public".

And there's a few things you won't get with this code. First, only two of the five fixed disk drives are internal drives and three drives are external had disk drives, but you can't tell which. At least for sure. Second, drive E is of type "CDRom" but it is actually a writable CD/DVD combo drive. But again, you can't tell the difference with this code.

Do more with DriveInfo class

Now that you know how to get this information. you may experience other properties that DriveInfo class offers. Here's a list of a few interesting properties:

  • Info.AvailableFreeSpace
  • Info.DriveFormat
  • Info.Name
  • Info.TotalFreeSpace
  • Info.TotalSize

Get drives of particular type with Visual Basic.NET

Here is a slight modification to the code above to make it more practical to use in VB.NET. This code gets the required drive type as parameter and returns only matching drives, if any. You may use this code to get only USB memory drives or network mapped drives for example.

Public Sub GetDrivesOfType(ByRef VolumeLabel() As String, _
  ByRef PathToDrive() As String, ByRef IsDriveReady() As Boolean, _
  ByVal DriveTypeVal As DriveType)
  '
  ' Return drives of given type
  Dim Info As DriveInfo
  Dim ThisLetter As Char
  Dim Count As Integer

  Try
    Count = 0
    For Each Info In My.Computer.FileSystem.Drives
      If Info.DriveType = DriveTypeVal Then
        ReDim Preserve VolumeLabel(Count)
        ReDim Preserve PathToDrive(Count)
        ReDim Preserve IsDriveReady(Count)
        ThisLetter = CChar(Info.RootDirectory.FullName.Substring(0, 1))
        If Info.IsReady Then
          IsDriveReady(Count) = True
          VolumeLabel(Count) = Info.VolumeLabel
        Else
          IsDriveReady(Count) = False
          VolumeLabel(Count) = ""
        End If
        If Info.DriveType = DriveType.Network Then
          PathToDrive(Count) = GetNetDriveName(ThisLetter & ":")
        Else
          PathToDrive(Count) = ThisLetter & ":"
        End If
        Count += 1
      End If
    Next Info
  Catch ex As Exception
    ' Error handling
  End Try

End Sub

and you call this procedure:

ReDim VolumeLabel(0)
ReDim PathToDrive(0)
ReDim IsDriveReady(0)
GetDrivesOfType(VolumeLabel, PathToDrive, IsDriveReady, DriveType.Removable)

to get all removable drives. If you want to get only USB memory sticks, remember that drives A and B are floppy disks. The output, if you use similar ListBox output as above, would look like this:

VolumeLabel; PathToDrive; IsDriveReady
; A:; False
PORTABLEAPP; I:; True

So, the only USB stick drive would have a drive letter "I".

Wednesday, September 24, 2008

Concatenate strings in VB.NET

VB.NET introduced a new way to concatenate strings, StringBuilder class. In VB.NET strings are immutable. This means that once a string is created its value can not be changed. So when your code concatenates strings, the strings themselves pointed by a string variable does not change. Instead a new string object is created and the string variable starts to reference this new string object.

Does this make any difference when concatenating strings in VB.NET code? Not really if you have a simple: MyString = "Hello " & "world!". But things get quite different when your code concatenates strings inside loops or you have otherwise excessive string manipulation in your code.

Concatenate strings with & -operator

Here's a simple procedure that makes concatenation in a loop with & -operator and finally shows elapsed time. Notice that the timing is done in a simple way. The timing in itself is not precise but enough to show the difference between two ways to concatenate strings.

Imports System.Text
''' <summary>
''' Concatenate strings with &amp; -operator
''' </summary>
''' <remarks></remarks>
Public Sub ConcatenateString()
  '
  Dim StartTime As DateTime
  Dim Elapsed As Double
  Dim ResultString As String
  Dim TempStr As String
  Dim i As Integer

  ResultString = ""
  TempStr = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit."
  StartTime = System.DateTime.Now
  For i = 0 To 9999
    ResultString = ResultString & "Line " & i & " " & TempStr & Environment.NewLine
  Next i
  Elapsed = System.DateTime.Now.Subtract(StartTime).TotalMilliseconds
  MessageBox.Show("Elapsed time " & Elapsed.ToString & " ms with & -operator", _
    "Elapsed Time", _
    MessageBoxButtons.OK, _
    MessageBoxIcon.Information)

End Sub

and the result is

Concatenate String with & -operator

Concatenate strings with StringBuilder class

Below is the same procedure as above. Now the concatenation of the strings is done with the StringBuilder object.

Imports System.Text
''' <summary>
''' Concatenate strings with StringBuilder
''' </summary>
''' <remarks></remarks>
Public Sub ConcatenateStringBuilder()
  '
  Dim StartTime As DateTime
  Dim Elapsed As Double
  Dim ResultString As String
  Dim TempStr As String
  Dim oStrBuilder As StringBuilder
  Dim i As Integer

  ResultString = ""
  TempStr = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit."
  oStrBuilder = New StringBuilder()
  StartTime = System.DateTime.Now
  For i = 0 To 9999
    oStrBuilder.Append("Line ")
    oStrBuilder.Append(i)
    oStrBuilder.Append(" ")
    oStrBuilder.Append(TempStr)
    oStrBuilder.Append(Environment.NewLine)
  Next i
  ' Move the result to ResultString variable
  ResultString = oStrBuilder.ToString
  Elapsed = System.DateTime.Now.Subtract(StartTime).TotalMilliseconds
  MessageBox.Show("Elapsed time " & Elapsed.ToString & " ms with StringBuilder", _
    "Elapsed Time", _
    MessageBoxButtons.OK, _
    MessageBoxIcon.Information)

End Sub

and now the result is

Concatenate String with StringBuilder

Conclusions

In the codes above the difference between methods was 17.5 seconds versus 0.015 seconds. So the conclusion is clear, StringBuilder class gave over 1 000 times faster results than & -operator. When your code concatenates strings inside loops or you have otherwise excessive string manipulation, use StringBuilder class to get most of your application.  But as I stated in the beginning of this post, & -operator is still useful. Using StringBuilder class in each and every simple concatenation of the strings would be an overkill.

But there's much more in StringBuilder class than just concatenating strings. Check Microsoft's reference for a complete list of the features StringBuilder class has to offer.

Monday, September 22, 2008

Using compiler directives with VB.NET

With VB.NET Microsoft introduced compiler directives to Visual Basic language. Compiler directives have been around for a long time in other languages and compilers and now they are also part of Visual Basic.

There are four compiler directives in VB.NET:

  • #Const directive
  • #ExternalSource directive
  • #If...Then...#Else directive
  • #Region directive

ExternalSource directive

Directive is used for mapping between specific lines of source code and external source text. See Microsoft's reference for information about this directive.

Region directive

With #Region "name"...#End Region directive you can organize source code to the blocks in the editor. These blocks have "+"-sign to "open" the block and "-"-sign to "hide" the block. When a block is hidden, only the "+"-sign followed by region's name are visible. This is very handy if the source file is long. You can put for example all helper routines to "helper"-region and form's event handlers to "form"-region. When blocks are collapsed, all you see is "helper" and "form" titles instead of lots of source code.

Const directive

This directive is commonly used in conjunction with #If directive. With #Const directive you can assign compiler variables which you can test with #If...Then...#Else directive.

#Const directives can be set from Project Properties to be global constants i.e. they are available in all project's modules. However, this is not possible with VB.NET Express Edition. If you are using Express Edition, #Const directive is only available in the module where it is defined.

If...Then...Else directive

#If...Then...#Else directive is the most flexible and the most useful VB.NET compiler directive. Below are a few samples for what you can do with this directive.

Conditional compiling:

#Const TargetOS = "Vista"

#If TargetOS = "Vista" Then
   ' Windows Vista specific code
#ElseIf TargetOS = "WinXP" Then
   ' Windows XP specific code
#Else
   ' Code for other OS
#End if

With conditional compiling you can write operating system, processor specific or other target platform specific code to the same source file. Changing #Const directive value and recompiling code gives you platform specific executables without maintaining multiple source codes for the same application.

Simple localization:

#Const Language = "French"

#If Language = "French" Then
   ' Show message to user in French
#Else
   ' Show message to user in English
#End if

If you have a simple user interface in your application or otherwise do not need a full localization, you can make a simple localization with conditional compiling.

Debugging code in source code:

#Const DebugMode = True

Public Sub DoSomething(ByVal ArgNumber As Integer)
#If DebugMode = True Then
  Debug.WriteLine("Entered sub DoSomething with argument: " & ArgNumber)
#End If
  ' Rest of procedure code

End Sub 

When you are on the development and coding phase of the application, you usually have more or less code for debugging purpose. Debugging code is something that you must never leave in the final production code. And debug dumps are something that your customers should never see. Again, conditional compiling is an easier solution than maintaining multiple source codes for the same application.