In my last post I discussed the approach we used at my job to convert an ASP .NET web forms application written in VB .NET to C#. In that post I alluded to some of the issues that we encountered during the conversion but didn’t delve into any of the technical details. I decided to take the technical specifics of some of the more interesting conversion issues and make a separate post of them. If you ever embark on a conversion project like ours these might be of use to you.
Option Strict and Option Explicit
This isn’t a conversion issue per se, but if you want to convert a VB .NET project to C# you definitely want to have these compiler settings turned on in your VB .NET project prior to doing so. Turning these compiler flags on forces you to both explicitly declare all variables before using them (Option Explicit) and disallows some of the implicit data type conversions that VB .NET will do for you (Option Strict). Having both of these flags turned on will make the VB .NET code behave more like its C# equivalent with respect to variable declaration and type conversion. That said, if you’ve been developing in VB .NET without these settings on you might not be the type of person that would really want to convert that code to C# anyway.
Implicit Data Type Conversions
Even with Option Strict turned on VB .NET is more permissive with implicit data type conversions than C#. For example, in VB .NET the following code will compile with Option Strict turned on:
2: 'Enumeration for defining columns in a grid
3: Public Enum OrderGridColumn
7: End Enum
9: 'When binding data to the grid:
10: gridRow.Columns(OrderGridColumn.OrderDate).Text = data.OrderDate.ToString("g", CultureInfo.CurrentCulture)
Here we’re using an enumeration to name the indexes of the columns in a grid for displaying order data. At runtime we’re using these values to access the grid’s columns and do special formatting for the OrderDate field. This is a somewhat trivial example, but we had lots of code like this scattered throughout our VB .NET project. The C# conversion tool converted line 10 of the above snippet to:
gridRow.Columns[OrderGridColumn.OrderDate].Text = data.OrderDate.ToString("g", CultureInfo.CurrentCulture);
Since the indexer on the Columns field of the gridRow takes an integer as a parameter, the C# compiler complains about not being able to implicitly cast the OrderGridColumn value to an int. VB .NET, on the other hand, had no issue with that implicit conversion. Immediately after using the automated conversion tool on our VB .NET project we had tons
of C# compiler errors related to this implicit data conversion. Thankfully this was pretty easily fixed with a regular expression and Visual Studio’s find/replace feature to shim in the explicit cast to an int:
gridRow.Columns[(int)OrderGridColumn.OrderDate].Text = data.OrderDate.ToString("g", CultureInfo.CurrentCulture);
For the record, there isn’t much that I like about VB .NET, but being able to implicitly cast enum values to integers is one thing that I miss.
Query-style LINQ expressions and Lambdas
VB .NET supports query-style LINQ expressions just like C# but with slightly different syntax. The automated conversion tool completely whiffed on converting these queries from VB .NET to C#. I ended up re-writing them to use the LINQ extension methods and the converter did better with those but still got choked up on some of the more complicated lambdas. If I had it to over again I would probably have hunted down these bits of VB .NET code with query expressions/heavy lambda use and re-written them to use simple loops prior to the conversion. After the conversion I could have then just let ReSharper do the LINQ refactoring for me.
Explicit Return Statements
In VB .NET you can return a value from a function or property getter by assigning the return value to the name of the property or method, like this:
1: Public Function Add(ByVal firstNumber As Integer, ByVal secondNumber As Integer) As Integer
2: Dim result as Integer = firstNumber + secondNumber
3: Add = result
4: Exit Function 'You don't technically need the explicit Exit Function in this case
5: End Function
Obviously C# doesn’t use this convention and requires an explicit return statement for all possible execution paths of a method or property getter. The automated conversion tool we used didn’t always properly convert to explicit return statements so I ended up changing places in VB .NET where we were using this convention to use explicit return statements instead.
When Clauses In Catch Blocks
VB .NET allows the use of ‘When’ clauses when catching exceptions to define multiple catch blocks that are used only when the condition in the When clause is true. C# doesn’t really have an equivalent way of doing this, so you have to pay some special attention to any Catch…When blocks when converting VB .NET to C#. In most cases the same run-time behavior can be achieved in C# by catching the desired exception types and then using switch or if/else statements to branch the handling logic.
For example, if you had the following VB .NET code:
2: 'Some code that might throw an exception
3: Catch ex As SqlException When ex.ErrorCode = SomeErrorCode
4: 'handle this error code
5: Catch ex As SqlException When ex.ErrorCode = SomeOtherCode
6: 'handle the other error code
7: End Try
The equivalent C# would be:
3: //some code that might throw an exception
5: catch (SqlException ex)
7: if(ex.ErrorCode == SomeErrorCode)
8: //handle this error code
9: else if (ex.ErrorCode == SomeOtherCode)
10: //handle the other error code
Note that we need to re-throw the exception in the ‘else’ condition to completely mirror the VB .NET catch blocks, as the exception would not have been caught at all if it didn’t meet one of the error code conditions.
Divide By Zero Errors
The last issue I want to discuss in this post was by far the strangest and least expected of all of the issues that we encountered while reviewing and testing the converted code.
Consider the following snippet of VB .NET code:
1: Dim dividend As Integer = 1
2: Dim divisor As Integer = 0
3: Dim quotient As Double = dividend / divisor
This code will print ‘Infinity’ to the console. Now consider the following equivalent C# code:
1: int dividend = 1;
2: int divisor = 0;
3: double quotient = dividend/divisor;
This code will throw a System.DivideByZeroException. The reason this happens is because of the difference in the way that VB .NET and C# interpret the “/” mathematical operator. In C# the “/” operator performs division that is consistent with the types of the dividend and divisor. In the C# snippet above the dividend and divisor are both typed as integers so the “/” operator is performing integer division and therefore throwing an exception. In VB .NET on the other hand the “/” operator always does division that will return the full quotient including any remainder value in the fractional portion (see http://msdn.microsoft.com/en-us/library/25bswc76(v=vs.80).aspx). When the division operation returns a floating point number it has the flexibility to indicate that the value approaches infinity (even if that’s not technically what the result of that division is).
To help deal with this VB .NET also exposes a “\” operator that will always return an integer result. If we were to re-write the VB .NET snippet above to use the the “\” operator for division instead it would also through a DivideByZeroException. Similarly if we were to tweak the C# code to type the dividend and/or divisor as a ‘double’ it would write ‘Infinity’ to the console. Note that the the ‘Infinity’ value is actually the PositiveInfinity constant exposed by the double data type.
If you’re interested there are a couple of questions on Stackoverflow and the Programmers Stack Exchange site that explore different behaviors for dividing by zero in programming languages: