Convert Between VB.NET and C#
Conversion between VB.NET and C# involves much more than adding or removing semi-colons and curly braces. Learn about differences between the two languages and how to simulate unique features of one language in the other.
- By Dave Doknjas
P>Technology Toolbox: VB.NET, C#
VB.NET and C# are so similar in expressive power and features that conversion between the two at first glance appears to be little more than adding or removing semicolons and curly braces. In fact, there are substantial differences between the two languages that you should be aware of. I'll discuss some of the more significant differences between VB.NET and C# (see Figure 1), and I'll provide some advice about how you can better ensure that your code has equivalent runtime behavior in both languages. Good conversion software will manage these differences for you, but you'll find it invaluable to have a good grasp of these issues if you're developing software in both languages or converting from one to the other.
Setting "Option Strict On" increases the type safety of your VB.NET code, but it might come as a surprise (or a genuine shock to C# programmers) that VB.NET's enforced type safety is actually stricter than that imposed by C#. For example, this line is not allowed in VB.NET with Option Strict set:
Dim thisInteger As Integer = 12 / 4
However, this equivalent C# line is perfectly acceptable, even at the highest compiler warning level:
int thisInteger = 12 / 4;
Adjustments to VB.NET code converted from C# are often necessary in order to raise it to the stricter level of type safety demanded by Option Strict.
In VB.NET, you specify the upper bound of an array in a fixed-size array initialization, while you specify the number of elements in C#. Therefore, you need to adjust all fixed-size array initialization statements regardless of the language you're converting to.
.NET offers many collections and lists that are fine automatically resizable alternatives to arrays (the most obvious is ArrayList), but there will be times when you might want to do a good old-fashioned ReDim Preserve on an array in C#. Here's how you can spell out in C# what VB.NET does for you behind the scenes:
ReDim Preserve x(NewUpperBound)
//the following is not pretty, but it
//does simulate VB's ReDim Preserve:
int temp = new int[NewUpperBound + 1];
if (x != null)
x = temp;
The ending condition of a VB.NET for loop is evaluated only once, while the C# for loop ending condition is evaluated on every iteration. If the ending condition is not a constant expression, then you should assign it to a temporary variable and use that variable in the ending condition to ensure the same behavior as the equivalent VB.NET code.
You cannot always translate C# for loops to VB.NET for loops because C# for loops are potentially much more complex. They are not limited to a single initialization, a single ending condition, and a single increment statement. Any C# for loop not conforming to these restrictions will have to be rewritten in an alternative loop format in VB.NET:
//this 'for' loop cannot be replaced by
//a VB 'For' loop:
for (i = 10, j = 1; i > 0 && j < 5; i--,
i = 10
j = 1
Do While i > 0 AndAlso j < 5
i -= 1
j += 1
Simulate VB.NET Features in C#
You can always convert VB.NET methods with optional parameters to overloaded methods (in either VB.NET or C#). However, it's easy to overlook the need to adjust the corresponding calls to these methods. SomeMethod( , ArgTwo) is allowed in VB.NET when calling a method with two or more optional parameters, but not in C#, where you need to insert the default values for the parameters prior to the last argument. In addition, if the client code calling the method is VB.NET code, you might want to continue to allow the client programmer the option of bypassing some of the optional parameters. If the method is coded in C#, the only way to allow a VB programmer this option when referencing your C# assembly is to preface each of the method's parameters with the System.Runtime.InteropServices.Optional attribute.
Contrary to popular belief, the VB Module is reproducible in C#. In C#, the Module is an internal sealed class with static fields and methods and a single private parameterless constructor to prevent instantiation. Every VB programmer understands the nature of VB Modules without necessarily knowing the non-module alternative. You can't instantiate or inherit Modules, and you can access their members only through one shared global instance. Also, you don't have to qualify a member reference with the Module name; for example, you can use ThisMethod() in VB instead of ThisModule.ThisMethod(), provided there are no method name conflicts.
You cannot declare local static variables in C#. Variables maintaining their state across method calls are easily simulated with class-level fields (static fields if the method is Shared), but C# cannot enforce the association with only one method. This is a case where the expressive power of VB.NET is greater than that of C#.
C# has no equivalent to the "With" construct. A popular alternative is to declare an additional reference with an abbreviated name, assign the original object to that reference, and pass all member calls to the object with the abbreviated name. This serves the main purpose of "With," which is to save typing ("With" improves performance only in pre-.NET versions of VB). This approach is actually more versatile because you can apply it to numerous variables simultaneously:
//this serves the same purpose as a VB
//'With' block, but can be applied
//simultaneously to many variables:
ObjectType s = SourceObject;
ObjectType r = RecipientObject;
r.IntegerProperty = s.IntegerProperty;
r.DoubleProperty = s.DoubleProperty;
Simulate C# Features in VB.NET
The thorniest issue you'll encounter when converting from C# to VB.NET involves assignments within expressions, which C# allows, but VB.NET does not. Here's how an assignment within a "while" loop header is translated to VB.NET:
//this code assigns a value to "foo"
//within the loop test,
//before testing its value:
while ((foo = getFoo()) > -1)
foo = getFoo()
Do While foo > -1
foo = getFoo()
Assignments within expressions can be much more complex and deeper within expressions than this simple example, so you won't find conversion software that attempts to rewrite these scenarios. Most conversion software inserts warnings where assignments within expressions are detected.
An "out" parameter in C# simply means that the argument can be passed in uninitialized, and the compiler forces you to assign a value to the parameter for every possible code path in the method. As with a "ref" parameter, the argument is still marshaled both to and from the method because the argument passed to an "out" parameter may or may not be initialized. There is no VB equivalent to the "out" keyword, but you can use the System.Runtime.InteropServices.Out parameter attribute in addition to the ByRef modifier (this attribute has no effect on a ByVal parameter). The parameter attribute exposes the parameter as an "out" parameter to a C# application and allows passing uninitialized arguments, but the VB.NET compiler doesn't force you to make an assignment to the parameter within the method. This useful feature of the C# compiler is missing in VB.NET.
C# offers the "as" keyword as a convenient and efficient way to cast an object to a type when you want to avoid exceptions caused by a cast failure. The cast result is null/Nothing when the cast is inappropriate:
foo = bar as SomeType;
In VB.NET, you can get the same effect with this (less elegant, but more descriptive) code:
foo = CType(IIf(TypeOf bar Is SomeType, _
bar, Nothing), SomeType)
Be Aware of Other Considerations
Many resources fail to mention the dual nature of the single pipe ("|") and single ampersand ("&") operators in C#, focusing only on the bitwise aspect of these operators. The single pipe is also the equivalent of the VB.NET logical "Or" operator, and the single ampersand is also the equivalent of the VB.NET logical "And" operator. If you use the short-circuit, logical, double pipe and double ampersand operators instead, the original behavior of the VB.NET operators is not reproduced, ignoring side effects generated by the VB.NET operators because both operands of the "And" and "Or" operators are always evaluated. The double pipe and double ampersand operators are equivalent to VB's short-circuit logical operators "OrElse" and "AndAlso." Some of the conversion challenges you face will involve sorting out the deceptively similar language features (see the sidebar, "Heed Deceptive Similarities," for two interesting special cases).
C# requires all possible code paths in a function to return a value explicitly, but VB.NET makes no such demand. If an exit point of a VB.NET function is reached without an explicit return statement, the return value is set to the most recent assignment to the implicit function name local variable (such as SomeFunction = True). Alternatively, the return value is set to the default value of the function return type if no assignment was made to the implicit function name variable. Ensuring that the C# code assigns the correct return value can be difficult because simply returning the default value prior to the end of the function results in different runtime behavior if any assignments to the implicit function name variable were made in the original code.
It's easy to overlook the need to adjust string literals when converting from C# to VB.NET because the converted code continues to compile in most cases. However, you need to examine all instances of the backslash character because they denote an escape sequence (except where part of a C# verbatim string). For example, the string literal "\\" means a single backslash in C#, but it means two backslashes in VB.NET. You must remove all instances of backslashes used to indicate such escape sequences in C#.
C# is case-sensitive and VB.NET is not, so you must make adjustments regardless of the direction of the conversion. For example, you're able to code "foo.tostring()" in VB.NET, while you need to use "foo.ToString()" in C#. In C#, you can have multiple identifiers at the same scope differing only in case, such as "abc" and "ABC," but you must rename one of them in the equivalent VB.NET code because VB.NET is case-insensitive.
VB.NET and C# are both appealing programming languages. VB.NET emphasizes clarity and descriptiveness, while C# emphasizes elegance and conciseness. Don't be deceived by the obvious superficial differences, such as semicolons and curly bracesbelow the surface are many issues you should understand if you're developing in both languages.