Previous | Index | Next 

[HOWTO] Apply ByVal keyword to unassigned by-reference parameters

By default, VB6 passes arguments to methods using by-reference semantics: VB6 assumes a ByRef keyword unless an explicit ByVal keyword is used. Conversely, VB.NET and C# use by-value semantics by default, and of course VB Migration Partner correctly accounts for this difference.

Because of the default passing mechanism, it turns out that a lot of VB6 methods use by-reference semantics only because the original developer forgot to add an explicit ByVal keyword, as in the following simple example:

 
        Function Add(one As Short, two As Short, prod As Long) As Integer
            prod = one * two
            Add = one + two 
        End Function

VB Migration Partner includes a sophisticated code analysis engine that spots which ByRef parameters might be better rendered using by-value semantics. The engine recognizes parameters that are indirectly assigned by passing them to other methods or as arguments of language statements such as Input or Get#. The result of such analysis is included in the converted application in the form of special remarks, such as the following one:

 
        Function Add(ByRef one As Short, ByRef two As Short, ByRef prod As Integer) As Short
            ' UPGRADE_INFO (#0551): The 'one' parameter is neither assigned in current method 
            ' nor is passed to methods that modify it. Consider changing its declaration using 
            ' the ByVal keyword.
            ' UPGRADE_INFO (#0551): The 'two' parameter is neither assigned in current method 
            ' nor is passed to methods that modify it. Consider changing its declaration using 
            ' the ByVal keyword.
            prod = one * two
            Return one + two
	End Function

Notice that no warning is issued for the third parameter, because it is being assigned inside the method. Also, no warnings are emitted for parameters of methods that handle events or are followed by an Implements clause, because you can’t change the syntax of such methods.

You can prevent VB Migration Partner from emitting these special comments by disabling warning #0551:

        '## project:DisableMessage 0551

or by disabling all the warnings related to code analysis:

        '## project:DisableMessages CodeAnalysis

These warnings are quite valuable during the migration of large applications. Not only using the ByVal keyword where appropriate improves your code quality, often makes it run faster, and sometime avoids subtle issues that can occur in the converted VB.NET code.

For example, the value of a property is always passed with by-value semantics under VB6, even if the receiving parameter is marked with an implicit or explicit ByRef keyword. This means that the value of the property is never changed (even if the parameter is assigned inside the method) and the Property Set method for that property is never invoked. Conversely, value properties passed to methods honor the parameter’s passing mechanism under VB.NET: even if the parameter is never modified inside the method, the property’s Set block is invoked on exiting the method.

This detail is often a problem with objects in the ADODB library, because many properties of the Connection and Recordset objects become read-only when the object is open. Passing such properties to a ByRef argument doesn’t cause an error under VB6, but it throws a runtime exception under VB.NET, because the property’s setter method invoked on exiting the method. The ability to detect whether a ByRef parameter can be safely converted into a ByVal parameter allows developers to reduce the number of such issues.

You can use the UseByVal pragma to automatically insert the ByVal keyword for all the parameters that would otherwise generate the abovementioned warning - that is all the parameters that unnecessarily adopt the by-reference mechanism. This pragma can be defined at the project, class, method, or parameter level; its argument can be one of these three values: 

No: don’t add the ByVal keyword to all parameters are under the scope of the pragma. (This value is useful to cancel the effect of a pragma with a broader scope.)

Yes: add the ByVal keyword to all parameters that aren’t assigned inside the method and that aren’t explicitly marked with the ByRef keyword. (This is the value assumed if the pragma has no arguments.) 

Force: use the ByVal keyword for all the parameters that aren’t assigned inside the method, even if they are explicitly marked with the ByRef keyword. To understand how this pragma works, consider the following VB6 code:

 
        '## UseByVal
        Function Add(ByRef one As Integer, two As Integer, prod As Long) As Integer
            prod = one * two
            Add = one + two 
        End Function

and the VB.NET code that VB Migration Partner generates:

 
        Function Add(ByRef one As Short, ByVal two As Short, ByRef prod As Integer) As Short
        ' UPGRADE_INFO (#0551): The 'one' parameter is neither assigned in current method
        ' nor is passed to methods that modify it. Consider changing its declaration using
        ' the ByVal keyword.
            prod = one * two
            Return one + two
        End Function

Notice that the first parameter isn’t affected because it has an explicit ByRef keyword, but a warning is emitted nevertheless. If you had used the a ByVal Force pragma, then VB Migration Partner would have generated this VB.NET code:

 
        Function Add(ByRef one As Short, ByVal two As Short, ByRef prod As Integer) As Short
        ' UPGRADE_INFO (#0551): The 'one' parameter is neither assigned in current method 
        ' nor is passed to methods that modify it. Consider changing its declaration using 
        ' the ByVal keyword.
            prod = one * two
            Return one + two
        End Function

 

Previous | Index | Next