The ability to store arrays in Variants was new to Visual Basic 4, and a number of new language elements were introduced to support them such as Array and IsArray.
To set up a Variant to be an array, you can either assign it to an already existing array or use the Array function. The first of these methods creates a Variant whose subtype is the array value (8192) added to the value of the type of the original array. The Array function, on the other hand, always creates an array of Variants-VarType 8204 (which is 8192 plus 12).
The following code shows three ways of creating a Variant array of the numbers 0, 1, 2, 3:
Dim v As Variant Dim a() As Integer Dim i As Integer ' Different ways to create Variant arrays ' 1. Use the Array function v = Array(0, 1, 2, 3) 'of little practical use v = Empty ' 2. Create a normal array, and assign it to a Variant. ' Iterate adding elements using a normal array... For i = 0 To 3 ReDim Preserve a(i) As Integer a(i) = i Next i ' ...and copy array to a Variant v = a 'or v = a() ' but not v() = a() v = Empty ' 3. Start off with Array, and then ReDim to preferred size ' avoiding use of intermediate array. For i = 0 To 3 ' First time we need to create array If IsEmpty(v) Then v = Array(i) Else ' From then on, ReDim Preserve will work on v ReDim Preserve v(i) End If v(i) = i Next i
Notice that the only difference between the last two arrays is that one is a Variant holding an array of integers and the other is a Variant holding an array of Variants. It can be easy to get confused here, look at the following:
ReDim a(5) As Variant
This code is creating an array of Variants, but this is not a Variant array. What consequence does this have? Not much anymore. Before version 6 you could utilize array copying only with Variant arrays, but now you can do this with any variable-sized array.
So what is useful about placing an array in a Variant? As Variants can contain arrays, and they can be arrays of Variants, those contained Variants can themselves be arrays, maybe of Variants, which can also be arrays, and so on and so forth.
Just how deep can these arrays be nested? I don't know if there is a theoretical limit, but in practice I have tested at least 10 levels of nesting. This odd bit of code works fine:
Dim v As Variant, i As Integer ' Make v an array of two Variants, each of which is an array ' of two Variants, each of...and so on For i = 0 To 10 v = Array(v, v) Next i ' Set a value... v(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0) = 23
How do these compare to more standard multidimensional arrays? Well, on the positive side, they are much more flexible. The contained arrays-corresponding to the lower dimensions of a multidimensional array-do not have to have the same number of elements. Figure 4-2 explains the difference pictorially.
Figure 4-2 The difference between a standard two-dimensional array (top) and a Variant array (bottom)
These are sometimes known as ragged arrays. As you can see from the diagram, we do not have all the wasted space of a multidimensional array. However you have to contrast that with the fact that the Variant "trees" are harder to set up.
This ability of Variants to hold arrays of Variants permits some interesting new data structures in Visual Basic. One obvious example is a tree. In this piece of code, an entire directory structure is folded up and inserted in a single Variant:
Private Sub Form_Load() Dim v As Variant v = GetFiles("C:\") ' Places contents of C: into v End Sub Public Function GetFiles(ByVal vPath As Variant) As Variant ' NB cannot use recursion immediately as Dir ' does not support it, so get array of files first Dim vDir As Variant, vSubDir As Variant, i vDir = GetDir(vPath) ' Now loop through array, adding subdirectory information. If Not IsEmpty(vDir) Then For i = LBound(vDir) To UBound(vDir) ' If this is a dir, then... If (GetAttr(vDir(i)) And vbDirectory) = vbDirectory Then ' replace dir name with the dir contents. vDir(i) = GetFiles(vDir(i)) End If Next i End If GetFiles = vDir End Function Private Function GetDir(ByVal vPath As Variant) As Variant ' This function returns a Variant that is an array ' of file and directory names (not including "." or "..") ' for a given directory path. Dim vRet As Variant, fname As Variant ' Add \ if necessary. If Right$(vPath, 1) <> "\" Then vPath = vPath & "\" ' Call the Dir function in a loop. fname = Dir(vPath, vbNormal & vbDirectory) Do While fname <> "" If fname <> "." And fname <> ".." Then vRet = AddElement(vRet, vPath & fname) End If fname = Dir() Loop ' Return the array. GetDir = vRet End Function Public Function AddElement(ByVal vArray As Variant, _ ByVal vElem As Variant) As Variant ' This function adds an element to a Variant array ' and returns an array with the element added to it. Dim vRet As Variant ' To be returned If IsEmpty(vArray) Then ' First time through, create an array of size 1. vRet = Array(vElem) Else vRet = vArray ' From then on, ReDim Preserve will work. ReDim Preserve vRet(UBound(vArray) + 1) vRet(UBound(vRet)) = vElem End If AddElement = vRet End Function
Using + for String ConcatenationOne point to note about this code is that this is an extremely efficient way of storing a tree structure, because as v is a multidimensional ragged array, the structure contains less wasted space than its equivalent multidimensional fixed-sized array. This contrasts with the accusation usually leveled at Variants, that they waste a lot of memory space.
This misconceived experiment with operator overloading was considered bad form even back in the days of Visual Basic 2, when the string concatenation operator & was first introduced. Yet it's still supported in Visual Basic 6. In particular, since version 4 brought in extensive implicit type conversion between numerics and strings, this issue has become even more important. It's easy to find examples of how you can get tripped up. Can you honestly be confident of what the following will print?Debug.Print "56" + 48 Debug.Print "56" + "48" Debug.Print "56" - "48"
What should happen is that adding two strings has the same effect as subtracting, multiplying, or dividing two strings-that is, the addition operator should treat the strings as numeric if it can; otherwise, it should generate a type mismatch error. Unfortunately, this is not the case. The only argument for why the operator stays in there, causing bugs, is backward compatibility.