Previous | Index | Next 

[PRB] .NET arrays are copied using "shallow" semantics

Array assignments work differently in VB6 and VB.NET. Under VB6, array assignments copy all the elements of the source array into the destination array; under VB.NET array assignments just copy the array pointer. This difference is a substantial one, as the following demonstrates:

        ' VB6
        Dim source(10) As Integer
        Dim dest() As Integer
        dest() = source()
        source(0) = 123 
        Debug.Print dest(0)    ' displays 0

        ' VB.NET
        Dim source(10) As Integer
        Dim dest() As Integer
        dest = source
        source(0) = 123 
        Debug.Print(dest(0))    ' displays 123 (both vars point to same element)

VB Migration Partner works around this difference by rendering all array assignments by means of the CloneArray6 helper method (in VB.NET) or the VB6Helpers.CloneArray method (in C#). In this specific case, the generated .NET code would be as follows:

        ' VB.NET
        Dim source(10) As Integer
        Dim dest() As Integer
        dest = CloneArray6(source)
        source(0) = 123 
        Debug.Print(dest(0))    ' displays 0 (preserves VB6 copy semantics)
        // C#
        int[] source = new int[11];
        int[] dest = null;
        dest = VB6Helpers.CloneArray(source);
        source[0] = 123;
        VB6Helpers.DebugPrintLine(dest[0]);   // displays 0 (preserves VB6 copy semantics)

In addition to enforcing the correct semantics with plain arrays, the CloneArray6 method ensures that arrays of structures are copied correctly, even if the structure contains arrays or nested structures.

Unfortunately, as sophisticated as it is, not even VB Migration Partner can automatically solve all the potential issues related to array copying. The following VB6 code shows an example of such issues:

        Dim values(0 To 1) As Integer
        Dim col As New Collection
        values(0) = 111
        col.Add(values)
        values(0) = 222
        col.Add(values)
        Debug.Print col(1)(0) & "," & col(2)(0)    ' displays "111,222"

Even if it isn’t immediately obvious, VB6 passes a copy of the array to the Add method, therefore you can later change the array elements without affecting the array that is already stored inside the collection. Conversely, VB.NET and C# pass a reference of the array to the Add method, thus the migrated code behaves quite differently:

        ...
        Debug.Print col(1)(0) & "," & col(2)(0)    ' displays "222,222"

You can work around this language difference in at least two ways, both of which require that you modify the VB6 code:

  1. You explicitly ReDim the array after the Add method, to force the compiler to create a new array reference:
            Dim values(0 To 1) As Integer
            Dim col As New Collection
            values(0) = 111
            col.Add(values)
            ReDim values(0 To 1)
            values(0) = 222
            col.Add(values)
            ReDim values(0 To 1)
            ...
    This solution works well, however it adds a small overhead to the VB6 code that isn’t strictly necessary.
  2. you use the CloneArray6 method inside the call to the Add method. The VB6 version of this method does nothing, but when the code is migrated to .NET the method ensures that a copy of the array is passed to the Add method:
            Dim values(0 To 1) As Integer
            Dim col As New Collection
            values(0) = 111
            col.Add(CloneArray6(values))
            values(0) = 222
            col.Add(CloneArray6(values))
    You also need to manually edit the VB6 code to insert an explicit call to the CloneArray6 method defined in VBMigrationPartner_Support module when copying an array of arrays – that is, a Variant array whose elements are arrays. Consider this VB6 code, which makes it evident that VB6 performs a “deep” copy of the array of arrays, i.e. it makes a copy of each individual array element:
            ' create an array of arrays and init one of its elements
            Dim source(10) As Variant
            Dim ints(5) As Integer
            source(1) = ints
            source(1)(1) = 111
            ' copy to a different array
            Dim dest() As Variant
            dest() = source()
            dest(1)(1) = 222
            Debug.Print source(1)(1) & "," & dest(1)(1)   ' displays "111,222"
    VB Migration Partner migrates the above code to .NET as follows:
            
            'VB.NET
            ' create an array of arrays and init one of its elements
            Dim source(10) As Object
            Dim ints(5) As Integer
            source(1) = ints
            source(1)(1) = 111
            ' copy to a different array
            Dim dest() As Object
            dest = CloneArray6(source)
            dest(1)(1) = 222
            Debug.PrintLine6(source(1)(1) & "," & dest(1)(1)) ' displays "222,222"
            
           // C#
           // create an array of arrays and init one of its elements
           object[] source = new object[11];
           int[] ints = new int[6];
           source[1] = ints;
           source[1][1] = 111;
           // copy to a different array
           object[] dest = null;
           dest = VB6Helpers.CloneArray(source);
           dest[1][1] = 222;
           VB6Helpers.DebugWriteLine(source[1][1] + "," & dest[1][1]); // displays "222,222"
    The problem in this case is that, by default, the CloneArray6 method performs a “shallow” copy – in other words it creates a copy of the source array into a different array, but the individual elements of the result array still point to the values stored in the source array.

    You can solve this problem by passing True in the second argument of the CloneArray6 method, which forces the method to perform a deep copy.
            ' ... in the original VB6 code...
            dest() = CloneArray6(source(), True)
    
    Notice that a deep copy is significantly slower than a shallow copy, therefore you should pass True in the second argument only if you have ascertained that the converted .NET doesn’t behave like the original code.

 

Previous | Index | Next