开发者

Working with arrays in VBA

I'm having a bit of a problem working with arrays in VBA, where the same would be trivial in (almost) any other language:

Public Function getArray() As MyType()
    'do a lot of work which returns an array开发者_如何学Go of an unknown length'
    'this is O(N^2) or something equally intensive so I only want to call this once'
End Function

Public Sub doSomething()
    Dim myArray() As MyType
    Set myArray = getArray() 'FAILS with "Cannot assign to array error"'
End Sub

I think it might be that I need to define the length of the array in advance, or ReDim a dynamic array. But I don't know the length of the returned array ahead of time, and I'd like to avoid calling the function twice:

Public Sub doSomething()
    Dim myArray(0 To UBound(getArray()) As MyType  'not entirely sure if this would work, but it involves calling getArray twice which I'd like to avoid
    Set myArray = getArray()
End Sub

In C# or Java the equivalent would be:

public MyType[] getArray(){
    //do some work and return an array of an unknown length
}

public void doSomething(){
    MyType[] myArray;
    myArray = getArray();  //one line and I don't need to define the length of array beforehand
}


When assigning arrays of custom objects in vba you need to pass them around as variants I've included a full working sample.

Class Module named MyType:

Public Once As Integer
Public Twice As Integer
Public Thrice As Integer

Code in standard module:

Public Function getArray() As MyType()
Dim i As Integer, arr() As MyType
    'do a lot of work which returns an array of an unknown length'
    'this is O(N^2) or something equally intensive so I only want to call this once'
    For i = 0 To Int(Rnd() * 6) + 1
        ReDim Preserve arr(i)
        Set arr(i) = New MyType
        arr(i).Once = i
        arr(i).Twice = i * 2
        arr(i).Thrice = i * 3
    Next i
    getArray = arr
    MsgBox "Long process complete"
End Function

Public Sub doSomething()
Static myArray() As MyType
Dim i As Integer
    If UBound(myArray) = -1 Then
        myArray = getArray()
    End If
    For i = LBound(myArray) To UBound(myArray)
        Debug.Print myArray(i).Once & vbTab & _
                    myArray(i).Twice & vbTab & _
                    myArray(i).Thrice
    Next i
End Sub

Public Sub Test()
Dim i As Integer
    For i = 1 To 3
        Debug.Print "Run Number " & i & vbCrLf & String(10, "-")
        doSomething
        Debug.Print
    Next i
End Sub

The first time you run doSomething an array of random length will be generated and you will see a message box that says "Long process complete". Subsequent calls to doSomething will re-use the array created the first time.

If you copy this code and just run the Test sub it will call doSomething three times. You will see the message box once and the output of doSomething in the immediate window three times.


Well, you could pass the array as a reference to the function like this:

Public Sub MyFunc(ByRef arr() As MyType)
  ...
End Sub


Dim myArr() as MyType
MyFunc myArr

Inside the function you can ReDim your array as wanted.


It is indeed possible to return an array from a function in VBA. According to MSDN:

[Y]ou can also call a procedure that returns an array and assign that to another array. [ . . . ] Note that to return an array from a procedure, you simply assign the array to the name of the procedure.

So you just need to modify your existing code by removing Set from the assignment statement:

Public Function getArray() As MyType()
    'do a lot of work which returns an array of an unknown length'
    'this is O(N^2) or something equally intensive so I only want to call this once'
End Function

Public Sub doSomething()
    Dim myArray() As MyType
    myArray = getArray
End Sub


I think you just need to get rid of the set in Set myArray = getArray()


This behaves properly:

Option Explicit

Public Function getArray() As Integer()
    Dim test(1 To 5) As Integer
    test(1) = 2
    test(2) = 4
    getArray = test
End Function

Public Sub doSomething()
    Dim myArray() As Integer
    myArray = getArray()
    Debug.Print (myArray(2))
End Sub
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜