C# Features

This section covers transformations that are applied to enhance certain elements with C# features.

1. Expression-bodied members.

Expression-bodied members refer to a more concise syntax for defining class members, such as properties or methods, using an expression instead of an entire block of code.

1.1. On

When choosing this option, the VBUC will apply a special pattern recognition mechanism to generate expression-bodied members when necessary.

Original VB6 Code

Public Property Get Name() As String
    Name = m_name
End Property

Public Property Let Name(newName As String)
    m_name = newName
End Property

Public Property Get Age() As String
    Age = m_age
End Property

Public Property Let Age(newValue As String)
    Dim newValue2 As Integer
    newValue2 = CInt(newValue)
    If newValue2 > 0 And newValue2 < 12 Then
        m_age = "Enfant"
    ElseIf newValue2 >= 12 And newValue2 < 18 Then
        m_age = "Teenager"
    ElseIf newValue2 >= 18 And newValue2 < 60 Then
        m_age = "Adult"
    Else
        m_age = "Elder"
    End If
End Property

Public Property Get Nationality() As String
    Nationality = "Costa Rican"
End Property

C# Code

private string m_name = "";
private string m_age = "";


public string Name
{
	get => m_name;
	set => m_name = value;
}



public string Age
{
	get => m_age;
	set
	{
		int newValue2 = Convert.ToInt32(Double.Parse(value));
		if (newValue2 > 0 && newValue2 < 12)
		{
			m_age = "Enfant";
		}
		else if (newValue2 >= 12 && newValue2 < 18)
		{ 
			m_age = "Teenager";
		}
		else if (newValue2 >= 18 && newValue2 < 60)
		{ 
			m_age = "Adult";
		}
		else
		{
			m_age = "Elder";
		}
	}
}


public string Nationality => "Costa Rican";

1.2. Off

Choosing this option, the VBUC will generate blocks of code as default.

Original VB6 Code

Public Property Get Name() As String
    Name = m_name
End Property

Public Property Let Name(newName As String)
    m_name = newName
End Property

Public Property Get Age() As String
    Age = m_age
End Property

Public Property Let Age(newValue As String)
    Dim newValue2 As Integer
    newValue2 = CInt(newValue)
    If newValue2 > 0 And newValue2 < 12 Then
        m_age = "Enfant"
    ElseIf newValue2 >= 12 And newValue2 < 18 Then
        m_age = "Teenager"
    ElseIf newValue2 >= 18 And newValue2 < 60 Then
        m_age = "Adult"
    Else
        m_age = "Elder"
    End If
End Property

Public Property Get Nationality() As String
    Nationality = "Costa Rican"
End Property

C# Code

private string m_name = "";
private string m_age = "";


public string Name
{
	get
	{
		return m_name;
	}
	set
	{
		m_name = value;
	}
}



public string Age
{
	get
	{
		return m_age;
	}
	set
	{
		int newValue2 = Convert.ToInt32(Double.Parse(value));
		if (newValue2 > 0 && newValue2 < 12)
		{
			m_age = "Enfant";
		}
		else if (newValue2 >= 12 && newValue2 < 18)
		{ 
			m_age = "Teenager";
		}
		else if (newValue2 >= 18 && newValue2 < 60)
		{ 
			m_age = "Adult";
		}
		else
		{
			m_age = "Elder";
		}
	}
}


public string Nationality
{
	get
	{
		return "Costa Rican";
	}
}

2. Generate Auto Implemented Properties

2.1. On

Auto-implemented properties will be generated when the original VB6 properties follow the basic standard pattern.

This feature verifies if there is a property defined on a local private field (both shares the same type) and it removes the field declarations and just leaves an auto-implemented property, replacing any reference to the field with a reference to the property in the class.

This feature is available for C# only.

Original VB6 code:

Private IntVar As Integer
Private BoolVar As Boolean
Private StrVar As String

Public Property Let IntVarProp(value As Integer)
    'some comments for IntVarProp property header'
    IntVar = value
    'some comments for IntVarProp property footer'
End Property
Public Property Get IntVarProp() As Integer
    'some comments for IntVarProp property header'
    IntVarProp = IntVar
    'some comments for IntVarProp property footer'
End Property
Public Property Let BoolVarProp(value As Boolean)
    BoolVar = value
End Property
Public Property Get BoolVarProp() As Boolean
    BoolVarProp = BoolVar
End Property
Public Property Let StrVarProp(value As String)
    StrVar = value
End Property
Public Property Get StrVarProp() As String
    StrVarProp = StrVar
End Property

Public Sub FieldsUsage()
    IntVar = 0
    BoolVar = False
    StrVar = "Hello World"
    parameterFieldsUsage IntVar, BoolVar, StrVar
End Sub

Public Sub PropertiesUsage()
    IntVarProp = 0
    BoolVarProp = False
    StrVarProp = "Hello World"
    parameterProperiesUsage IntVarProp, BoolVarProp, StrVarProp
End Sub

Public Sub parameterFieldsUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

Public Sub parameterProperiesUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

C# code:

public int IntVarProp
{
	//some comments for IntVarProp property header
	get;
	//some comments for IntVarProp property footer
	//some comments for IntVarProp property header
	set;
	//some comments for IntVarProp property footer
}

public bool BoolVarProp
{
	get;
	set;
}

public string StrVarProp
{
	get;
	set;
}


public void FieldsUsage()
{
	this.IntVarProp = 0;
	this.BoolVarProp = false;
	this.StrVarProp = "Hello World";
	parameterFieldsUsage(this.IntVarProp, this.BoolVarProp, this.StrVarProp);
}

public void PropertiesUsage()
{
	IntVarProp = 0;
	BoolVarProp = false;
	StrVarProp = "Hello World";
	parameterProperiesUsage(IntVarProp, BoolVarProp, StrVarProp);
}

public void parameterFieldsUsage(int IntVar, bool BoolVar, string StrVar)
{
	//
}

public void parameterProperiesUsage(int IntVar, bool BoolVar, string StrVar)
{
	//
}

2.2. Off

No Auto-Implemented Properties will be generated from the original VB6 ones.

Original VB6 code:

Private IntVar As Integer
Private BoolVar As Boolean
Private StrVar As String

Public Property Let IntVarProp(value As Integer)
    'some comments for IntVarProp property header'
    IntVar = value
    'some comments for IntVarProp property footer'
End Property
Public Property Get IntVarProp() As Integer
    'some comments for IntVarProp property header'
    IntVarProp = IntVar
    'some comments for IntVarProp property footer'
End Property
Public Property Let BoolVarProp(value As Boolean)
    BoolVar = value
End Property
Public Property Get BoolVarProp() As Boolean
    BoolVarProp = BoolVar
End Property
Public Property Let StrVarProp(value As String)
    StrVar = value
End Property
Public Property Get StrVarProp() As String
    StrVarProp = StrVar
End Property

Public Sub FieldsUsage()
    IntVar = 0
    BoolVar = False
    StrVar = "Hello World"
    parameterFieldsUsage IntVar, BoolVar, StrVar
End Sub

Public Sub PropertiesUsage()
    IntVarProp = 0
    BoolVarProp = False
    StrVarProp = "Hello World"
    parameterProperiesUsage IntVarProp, BoolVarProp, StrVarProp
End Sub

Public Sub parameterFieldsUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

Public Sub parameterProperiesUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

C# code:

private int IntVar = 0;
private bool BoolVar = false;
private string StrVar = "";

public int IntVarProp
{
	get
	{
		//some comments for IntVarProp property header
		return IntVar;
		//some comments for IntVarProp property footer
	}
	set
	{
		//some comments for IntVarProp property header
		IntVar = value;
		//some comments for IntVarProp property footer
	}
}

public bool BoolVarProp
{
	get
	{
		return BoolVar;
	}
	set
	{
		BoolVar = value;
	}
}

public string StrVarProp
{
	get
	{
		return StrVar;
	}
	set
	{
		StrVar = value;
	}
}

public void FieldsUsage()
{
	IntVar = 0;
	BoolVar = false;
	StrVar = "Hello World";
	parameterFieldsUsage(IntVar, BoolVar, StrVar);
}

public void PropertiesUsage()
{
	IntVarProp = 0;
	BoolVarProp = false;
	StrVarProp = "Hello World";
	parameterProperiesUsage(IntVarProp, BoolVarProp, StrVarProp);
}

public void parameterFieldsUsage(int IntVar, bool BoolVar, string StrVar)
{
	//
}

public void parameterProperiesUsage(int IntVar, bool BoolVar, string StrVar)
{
	//
}

3. GoTo/GoSub Conversion

GoSub statements can be transformed to local functions only when C# is the target language.

3.1. Do not convert Go Sub statements

Choosing this option, the VBUC will not apply any special pattern recognition mechanism. GoSub statements are not supported by default in .NET structured code.

Original VB6 code:

Public Sub Main()

Dim i As Integer
i = 4
If i = 1 Then
	GoSub printOne
ElseIf i = 2 Then
	GoSub printTwo
ElseIf i = 3 Then
	GoSub printThree
Else
	GoSub SomethingElse
End If
GoTo exitLabel

printOne:
MsgBox "Value = 1"
Return

printTwo:
MsgBox "Value = 2"
Return

printThree:
MsgBox "Value = 3"
Return

SomethingElse:
MsgBox "Invalid Value!"
Return

exitLabel:

End Sub

C# code:

public static void Main()
{
	int i = 4;
	if (i == 1)
	{
		//UPGRADE_ISSUE: (1014) GoSub statement is not supported.
		//UPGRADE_TODO: (1065) Error handling statement (Go Sub) could not be converted.
		UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("GoSub Label (printOne)");
	}
	else if (i == 2)
	{ 
		//UPGRADE_ISSUE: (1014) GoSub statement is not supported.
		//UPGRADE_TODO: (1065) Error handling statement (Go Sub) could not be converted.
		UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("GoSub Label (printTwo)");
	}
	else if (i == 3)
	{ 
		//UPGRADE_ISSUE: (1014) GoSub statement is not supported. 
		//UPGRADE_TODO: (1065) Error handling statement (Go Sub) could not be converted. 
		UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("GoSub Label (printThree)");
	}
	else
	{
		//UPGRADE_ISSUE: (1014) GoSub statement is not supported.
		//UPGRADE_TODO: (1065) Error handling statement (Go Sub) could not be converted.
		UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("GoSub Label (SomethingElse)");
	}
	return;

	printOne:
	MessageBox.Show("Value = 1", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	//UPGRADE_WARNING: (2081) Return has a new behavior.
	return;

	printTwo:
	MessageBox.Show("Value = 2", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	//UPGRADE_WARNING: (2081) Return has a new behavior.
	return;

	printThree:
	MessageBox.Show("Value = 3", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	//UPGRADE_WARNING: (2081) Return has a new behavior.
	return;

	SomethingElse:
	MessageBox.Show("Invalid Value!", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	//UPGRADE_WARNING: (2081) Return has a new behavior.
	return;
}

3.2. Convert only GoSub statements to C# local function

Choosing this option, the VBUC will apply the special pattern recognition mechanism, to support the GoSub statement. This feature will only be applied when generating C# code.

General Description:

Visual Basic 6.0 provides the ability to jump into the code from one portion to another thru "labels" and create code that is not structured, according to the suggested coding patterns.

However, using GoSub and return statements gives the VB6 programmer certain functionality that used with certain restrictions can create "structured code".

By turning this feature on, the VBUC will recognize some of these patterns and will convert them to local functions for C#.

Original VB6 code:

Public Sub Main()

Dim i As Integer
i = 4
If i = 1 Then
	GoSub printOne
ElseIf i = 2 Then
	GoSub printTwo
ElseIf i = 3 Then
	GoSub printThree
Else
	GoSub SomethingElse
End If
GoTo exitLabel

printOne:
MsgBox "Value = 1"
Return

printTwo:
MsgBox "Value = 2"
Return

printThree:
MsgBox "Value = 3"
Return

SomethingElse:
MsgBox "Invalid Value!"
Return

exitLabel:

End Sub

C# code:

public static void Main()
{
	int i = 4;
	if (i == 1)
	{
		printOne();
	}
	else if (i == 2)
	{ 
		printTwo();
	}
	else if (i == 3)
	{ 
		printThree();
	}
	else
	{
		SomethingElse();
	}
	return;

	void printOne()
	{
		MessageBox.Show("Value = 1", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	}

	void printTwo()
	{
		MessageBox.Show("Value = 2", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	}

	void printThree()
	{
		MessageBox.Show("Value = 3", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	}

	void SomethingElse()
	{
		MessageBox.Show("Invalid Value!", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
	}
}

3.3. Convert GoTo and GoSub statements to C# local function

Choosing this option, the VBUC will convert, when possible, GoTo and GoSub statements to internal functions. This feature is only available for C#.

Basic GoSub Statement

This feature will select GoSub/Return blocks to convert to functions. The label corresponding to a GoSub call must be at the same level as the Return statement. The GoSub statement will be replaced by a call to the function that checks for its return value to see if an exit condition was met (Exit Function or Exit Sub, for example).

Original VB6 Code

Public Sub BasicGoSubExample()
    Dim i As Integer
    i = 0
    MsgBox i ' Display 0
    GoSub Increase
    MsgBox i ' Display 1
    Exit Sub
Increase:
    i = i + 1
    Return
End Sub

Public Sub BasicGoSubExampleWithExit()
    Dim i As Integer
    i = 0
    MsgBox i ' Display 0
    GoSub Increase
    MsgBox i ' Will not display
    Exit Sub
Increase:
    i = i + 1
    If i = 1 Then
        Exit Sub
    End If
    Return
End Sub

C# Code

public void BasicGoSubExample()
{
	int i = 0;
	MessageBox.Show(i.ToString(), ...)); // Display 0
	if (label_Increase_function())
	{
		return;
	}
	MessageBox.Show(i.ToString(), ...)); // Display 1
	return;
	bool label_Increase_function()
	{

		i++;
		return false;
	}
}

public void BasicGoSubExampleWithExit()
{
	int i = 0;
	MessageBox.Show(i.ToString(), ...)); // Display 0
	if (label_Increase_function())
	{
		return;
	}
	MessageBox.Show(i.ToString(), ...)); // Will not display
	return;
	bool label_Increase_function()
	{

		i++;
		if (i == 1)
		{
			return true;
		}
		return false;
	}
}

Basic GoTo Statement

In order to convert GoTo statements to functions, the VBUC will look for a pattern that starts with the label, and could end in another label, a Return statement, an Exit statement, or another GoTo statement. These blocks will be extracted as internal functions an calls to them placed where the GoTo statements. Since GoTo statements are one-way, they will be replaced with a call to the function followed by a return statement (if it's not the last line of of code), because the code after a GoTo statement should not be executed. Some code after a GoTo statement will be detected as dead code and will be commented.

Original VB6 Code

Public Sub BasicGoToExample()
    Dim i As Integer
    i = 0
    MsgBox i ' Display 0
    GoTo Increase
    MsgBox i
Increase:
    i = i + 1
End Sub

Public Sub BasicGoToExampleWithTwoGoTos()
    Dim i As Integer
    i = 0
    MsgBox i ' Display 0
    GoTo Increase
    MsgBox i
Continue:
    MsgBox i ' Display 1
    Exit Sub
Increase:
    i = i + 1
    GoTo Continue
End Sub

C# Code

public void BasicGoToExample()
{
	int i = 0;
	MessageBox.Show(i.ToString(), ...)); // Display 0
	label_Increase_function();
	//UPGRADE_NOTE: (7001) The following code block (dead code detected by GoSub/GoTo feature) seems to be dead code...
	//MessageBox.Show(i.ToString(), ...));
	//label_Increase_function();
	bool label_Increase_function()
	{

		i++;
		return false;
	}
}

public void BasicGoToExampleWithTwoGoTos()
{
	int i = 0;
	MessageBox.Show(i.ToString(), ...)); // Display 0
	label_Increase_function();
	//UPGRADE_NOTE: (7001) The following code block (dead code detected by GoSub/GoTo feature) seems to be dead code...
	//MessageBox.Show(i.ToString(), ...));
	//label_Continue_function();
	//return;
	bool label_Continue_function()
	{

		MessageBox.Show(i.ToString(), ...)); // Display 1
		return true;
	}
	bool label_Increase_function()
	{

		i++;
		return label_Continue_function();
	}
}

This feature uses boolean return values to control the flow of the code so it is consistent with the original VB6 code. When one of these internal functions returns true, it is because the original method has ended (due to an Exit Sub or Exit Function), and so the return value is used to stop execution all the way back to the initical calling point. The way calls to internal functions are migrated varies depending on if the function is called from the main method, or from within another function.

Original VB6 Code

//Public Sub CodeFlowExample()
    Dim i As Integer
    i = 0
    MsgBox i
    GoSub Increase
    MsgBox i
    GoTo Decrease
    Exit Sub
Multiply:
    i = i * 2
    Return
Increase:
    i = i + 1
    GoSub Divide
    GoTo Multiply
Decrease:
    i = i - 1
    Exit Sub
Divide:
    If i > 0 Then
        i = i / 2
    Else
        Exit Sub
    End If
    Return
End Sub

C# Code

public void CodeFlowExample()
{
	int i = 0;
	MessageBox.Show(i.ToString(), ...));
	if (label_Increase_function())
	{
		return;
	}
	MessageBox.Show(i.ToString(), ...));
	label_Decrease_function();
	return;
	bool label_Multiply_function()
	{

		i *= 2;
		return false;
	}
	bool label_Increase_function()
	{

		i++;
		if (label_Divide_function())
		{
			return true;
		}
		return label_Multiply_function();
	}
	bool label_Decrease_function()
	{

		i--;
		return true;
	}
	bool label_Divide_function()
	{

		if (i > 0)
		{
			i = Convert.ToInt32(i / 2d);
		}
		else
		{
			return true;
		}
		return false;
	}
}

4. Discards

The discard feature was introduced in C# 7.0. It allows you to discard the result of an expression or variable you are not interested in or do not intend to use. Instead of assigning it to a variable you won't use, you can use the _ (underscore) symbol as a placeholder to discard the result.

4.1. On

When choosing this option, the VBUC will apply a special pattern recognition mechanism to apply the discard feature when necessary.

Original VB6 Code

Private Sub Form_Load()
    Foo 2, 4
End Sub

Private Sub Foo(x As Integer, y As Integer)
    Dim Sum As Integer
    Dim Prod As Integer
    Sum = x + y     ' Variable used on MsgBox
    Prod = x * y    ' Variable not used
    
    MsgBox Sum
End Sub

C# Code

private void Form_Load() => Foo(2, 4);


private void Foo(int x, int y)
{
	int Sum = x + y; // Variable used on MsgBox
	_ = x * y; // Variable not used

	MessageBox.Show(Sum.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

4.2. Off

Choosing this option, the VBUC will generate variables even if they are no longer necessary in the migrated code.

Original VB6 Code

Private Sub Form_Load()
    Foo 2, 4
End Sub

Private Sub Foo(x As Integer, y As Integer)
    Dim Sum As Integer
    Dim Prod As Integer
    Sum = x + y     ' Variable used on MsgBox
    Prod = x * y    ' Variable not used
    
    MsgBox Sum
End Sub

C# Code

private void Form_Load() => Foo(2, 4);


private void Foo(int x, int y)
{
	int Sum = x + y; // Variable used on MsgBox
	int Prod = x * y; // Variable not used

	MessageBox.Show(Sum.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

Last updated