The Visual Basic Upgrade Companion has the ability to generate either VB.NET or C#.NET source code. C# generation includes an important set of transformations related to:
A target syntax writer covering all the C# language features was created to print .cs files with their appropriate syntax. This section contains source code examples to demonstrate how the VBUC handles the common annoyances while upgrading to C#.
Several language constructions have a different structure in C#. These differences require particular transformation rules to achieve the appropriate distribution of the target elements. In other cases some constructions are completely missing in C# or have similar ones with a different behavior.
Some examples of these differences are:
While VB6 had a very permissive type system which resolved types in runtime, C# works with a much more efficient, strict typing system that require all the variables to be properly declared and most type coercions to be explicitly expressed in the source code.
Deducing all the typing information from the VB6 source code is a difficult task since it includes very complex relationships, and in several cases variables are used in an inconsistent way.
The VBUC’s typing mechanism infers a substantial amount of the implicit typing information, allowing a better conversion process by generating most of the required type castings and coercions.
Additionally a coercion engine was added to the VBUC to allow the generation of type corrections, which were not necessary for converting to VB.NET but are required for C#.
For a source code example visit section Resolve Late Bound Variable Types
VB6 control events are defined by name, and its usage is very simple. In C# there is some additional work needed to implement the equivalent structure. In this platform the method that handles the event actions needs to be attached to System.EventHandler to open a listener for that event.
This source sample demonstrates the transformations needed to upgrade a VB6 click event into a fully equivalent .NET event handler.
Note: the VB6 event has a specific naming pattern
<control>_<event name>
Private Sub Command1_Click()
Dim base As Integer
Dim index As Integer
Dim Result As Long
With Text1
If (IsNumeric(.Text) And IsNumeric(Text2.Text)) Then
base = CInt(.Text)
With Text2
index = CInt(.Text)
End With
Result = Module1.Power(base, index)
MyClass.storePower Result
MsgBox Result
Else
MsgBox "Incorrect values found", vbCritical, "Error!"
End If
End With
End Sub
The VBUC will generate the necessary code to handle the event contained into the form source code and will add into the designer code the attachment to a system.EventHandler:
Note: the gray highlight for the with structure removal
in the resulting source code.
this.Command1.Click += new System.EventHandler(this.Command1_Click);
private void Command1_Click( Object eventSender, EventArgs eventArgs)
{
int base_Renamed = 0;
int index = 0;
int Result = 0;
double dbNumericTemp2 = 0;
double dbNumericTemp = 0;
if (Double.TryParse(Text1.Text, NumberStyles.Number, CultureInfo.CurrentCulture.NumberFormat, out dbNumericTemp) && Double.TryParse(Text2.Text, NumberStyles.Number, CultureInfo.CurrentCulture.NumberFormat, out dbNumericTemp2)){
base_Renamed = Convert.ToInt32(Double.Parse(Text1.Text));
index = Convert.ToInt32(Double.Parse(Text2.Text));
Result = Module1.Power(base_Renamed, ref index);
MyClass.storePower(Result);
MessageBox.Show(Result.ToString(), Application.ProductName);
} else{
MessageBox.Show("Incorrect values found", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
The VB6 error handling model, including statements like “On Error Goto”, “On Error Resume Next” and “Resume”, are not supported in C#. Instead, the “Try ... Catch” structured error handling model should be used.
While not every single “On Error” pattern has an equivalent “Try ... Catch” structure, the VBUC automatically converts the most common used scenarios. The ability to write custom patterns and add them to the VBUC is also available.
For more information see the “Error Handling Transformation” section below.
C# does not support VB modules. VB6 modules are converted to classes with static members and the references to these members are qualified with the name of the converted class.
The parameter declaration and passing mechanisms in C# requires several transformations to work with an equivalent behavior as in VB6.
The following transformations were implemented to support the C# parameters model:
Lower bounds in VB6 are 1 by default. In C# they are always 0. Several transformations are applied to correct the arrays’ declarations and the indexes used while accessing either arrays or collections.
The VBUC considers the “Option Base” keyword to generate functionally equivalent .NET code that matches the behavior of the VB6 code.
Read the next section for a detailed code example.
C# arrays are quite inflexible. They only support lower bound equal to zero and arrays with a constant amount of dimensions (although they can change their magnitudes). However the .NET class Array allows representing flexible arrays that can be re-dimensioned, and their lower bounds changed.
The following modifications are supported by the VBUC:
<arrayref> = new <ArrayType>(<NewDimensions>)
If the array was declared by using the class “Array” an invocation to Array.CreateInstance is used to create the new array.
When the “preserve” keyword was used, a statement is generated after the assignment to copy the original information into the new array instance.
Public Sub ArrayDimReDim()
'declare the array and the result variable
Dim myTestArray() As Integer
Dim maxInt As Integer
'set the array dimensions
ReDim myTestArray(10)
'populate the array
myTestArray(returnIndex(1)) = 15
myTestArray(returnIndex(2)) = 78
myTestArray(returnIndex(3)) = 23
myTestArray(returnIndex(4)) = 9
myTestArray(returnIndex(5)) = 1
myTestArray(returnIndex(6)) = 5
myTestArray(returnIndex(7)) = 9
myTestArray(returnIndex(8)) = 95
myTestArray(returnIndex(9)) = 2
myTestArray(returnIndex(10)) = 5
'search and display the largest number in the array
maxInt = Module1.max(myTestArray)
MsgBox maxInt, , "MAX INT"
'New array dimansions
ReDim Preserve myTestArray(12)
'populate the newly created fields
myTestArray(11) = 0
myTestArray(12) = 200
'search again for the largest number on the array
maxInt = Module1.max(myTestArray)
MsgBox maxInt, , "MAX INT"
End Sub
The resulting source code contains corrected access indexes for the resulting .NET array and a particular solution to the “ReDim Preserve” keyword using .NET 2.0 generics:
static public void ArrayDimReDim()
{
//declare the array and the result variable
int[] myTestArray = null;
//set the array dimensions
myTestArray = new int[10];
//populate the array
myTestArray[returnIndex(1) - 1] = 15;
myTestArray[returnIndex(2) - 1] = 78;
myTestArray[returnIndex(3) - 1] = 23;
myTestArray[returnIndex(4) - 1] = 9;
myTestArray[returnIndex(5) - 1] = 1;
myTestArray[returnIndex(6) - 1] = 5;
myTestArray[returnIndex(7) - 1] = 9;
myTestArray[returnIndex(8) - 1] = 95;
myTestArray[returnIndex(9) - 1] = 2;
myTestArray[returnIndex(10) - 1] = 5;
//search and display the largest number in the array
int maxInt = Module1.max(myTestArray);
MessageBox.Show(maxInt.ToString(), "MAX INT");
//New array dimansions
myTestArray = ArraysHelper.RedimPreserve<int[]>(myTestArray, new int[]{12});
//populate the newly created fields
myTestArray[10] = 0;
myTestArray[11] = 200;
//search again for the largest number on the array
maxInt = Module1.max(myTestArray);
MessageBox.Show(maxInt.ToString(), "MAX INT");
}
In Visual Basic every form, class and user control has a default instance with the same name as the class. For example Form1 can reference the type or the default Form1 instance with the same name. The VBUC creates variables called <TypeName>_definstance, and changes all the corresponding references to this variable to reproduce the same behavior as in Visual Basic.
Private Sub Form_Load()
Form1.Caption = "This is the main Window"
End Sub
private void Form1_Load( Object eventSender, EventArgs eventArgs)
{
Form1.DefInstance.Text = "This is the main Window";
}
Indexer properties in Visual Basic are done through default properties called “Item”. In C#, the indexer properties are accessed by accessing the instance as an array. The index passed to this “array reference” is sent as a parameter to the “Item” method. The VBUC recognizes and modifies most of the indexer property cases.
Dim arr(100) As String
Public Property Get Item(index As Integer) As Variant
Item = arr(index)
End Property
Public Property Let Item(index As Integer, val As Variant)
arr(index) = val
End Property
public string this[int index]
{
get
{
return arr[index];
}
set
{
arr[index] = value;
}
}
Since C# does not have an equivalent for the With statement, the VBUC expands the missing references and removes the With statement.
Read section Event Declaration and Invocation for a code sample featuring the with structure removal
In VB6, to specify the return value for a function an assignment is performed to its name. A set of transformations patterns have been implemented into the VBUC to convert this assignments into return patterns.
In some cases the use of temporal variables is required, since the original VB6 assignment do not break the function body execution as return statements do.
The following example shows a very simple function which has different return values depending on the input. In the second condition the function name is assigned but it is not the last statement in the block of code
Public Function Power(x As Integer, n As Integer) As Integer
If (n = 0) Then
Power = 1
ElseIf (n < 1) Then
Power = 0
MsgBox "Negative power is not supported"
Else
Dim aux As Long
aux = x
While (n > 1)
aux = aux * x
n = n - 1
Wend
Power = aux
End If
End Function
The code generated by the VBUC features the “return” keyword and a temporary variable used to store the possible return values. Note that the assignments to the iteration variables was upgraded to a more .NET like pattern, see the gray highligth.
static public int Power( int x, ref int n){
int result = 0;
int aux = 0;
if (n == 0){
result = 1;
}
else if ((n < 1)) {
result = 0;
MessageBox.Show("Negative power is not supported", Application.ProductName);
}
else{
aux = x;
while ((n > 1)){
aux *= x;
n--;
}
result = aux;
}
return result;
}
This advanced functionality is similar to the VB.NET Interfaces conversion described in the respective section. However, it introduces some additional transformations for C#, where the method implementation mechanism is different.
In C#, instead of adding an <Implements Interface.Method> clause in the method declaration, the method implementation model is expressed implicitly through the class method names, which must match the interface method names. The VBUC renames both the method declarations and their references to make them match the interface method names.
For complete code examples please visit section Interfaces support.
C# is a case sensitive language, which means that identifiers must be corrected when converting from VB6 to C# in order to make all the references to the same element show exactly the same case-sensitive name as in its declaration.
Arrays in VB6 are accessed with round parenthesis, in the same way as subroutines and functions. For C# this array access expressions must be recognized, differentiating them from function invocations, and generated with the appropriate syntax.
Read section Array dims and redims for a complete code example.
Variables in C# must be initialized with default values to avoid recurring compilation errors and warnings about variables being used without having been previously initialized. The VBUC creates the necessary initialization values considering the declared type. Also the “As New <Type>” clause is converted to a declaration where the variable is initialized with a “new <Type>()” expression.
This code sample demonstrates how the VBUC is able to merge the variable declaration with its initialization value in order to generate pure C#.NET code. This example combines the use of strongly typed variables with late bound variables and type coercions.
Public Sub VarInit()
Dim integerVar
Dim singleVar As Single
Dim LongVar As Long
Dim floatVar As Double
Dim currencyVar As Currency
Dim dateVar As Date
byteVar = CByte(0)
singleVar = byteVar + 0.5
floatVar = singleVar + 0.31654879
stringVar = "0100"
integerVar = CInt(stringVar)
LongVar = integerVar
booleanVar = (integerVar > byteVar)
currencyVar = "250"
dateVar = Now
End Sub
static public void VarInit()
{
int byteVar = 0;
float singleVar = (float) (byteVar + 0.5d);
double floatVar = singleVar + 0.31654879d;
string stringVar = "0100";
int integerVar = Convert.ToInt32(Double.Parse(stringVar));
int LongVar = integerVar;
bool booleanVar = (integerVar > byteVar);
decimal currencyVar = 250;
System.DateTime dateVar = DateTime.Now;
}
8834 N Capital of Texas Hwy, Ste 302
Austin, TX 78759
Call us: +1 (425) 609-8458
info@wearegap.com