【发布时间】:2015-08-14 23:45:59
【问题描述】:
我正在尝试获取数组的长度,但我不断收到此错误:
需要对象
我做错了吗?
Dim columns As Variant
columns = Array( _
"A", "ID", _
"D", "Name")
Debug.Print columns.Length ' Error: Object required
【问题讨论】:
我正在尝试获取数组的长度,但我不断收到此错误:
需要对象
我做错了吗?
Dim columns As Variant
columns = Array( _
"A", "ID", _
"D", "Name")
Debug.Print columns.Length ' Error: Object required
【问题讨论】:
复制/粘贴解决方案:
最常见的答案是这样的:
UBound(myItems) - LBound(myItems) + 1
虽然它在 +90% 的时间内工作,但当客户端/用户运行它时,其他 10% 会因严重的计划外错误而失败。这是因为该解决方案没有涵盖许多边缘情况。
通用解决方案:
下面的解决方案涵盖了我迄今为止发现的所有边缘情况。它消除了客户端/用户运行时的所有运行时故障。
'Generic solution using Variant
Public Const SIZE_NOT_ARRAY As Long = -1
Public Const SIZE_EMPTY As Long = 0
'Return Value:
' -1 - Not an Array
' 0 - Empty
' > 0 - Defined
Public Function size( _
ByVal values As Variant _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Long
Dim result As Long: result = SIZE_NOT_ARRAY 'Default to not an Array
Dim lowerBound As Long
Dim upperBound As Long
On Error GoTo NormalExit
If (IsArray(values) = True) Then
result = SIZE_EMPTY 'Move default to Empty
lowerBound = LBound(values, dimensionOneBased) 'Possibly generates error
upperBound = UBound(values, dimensionOneBased) 'Possibly generates error
If (lowerBound < upperBound) Then
result = upperBound - lowerBound + 1 'Size greater than 1
Else
If (lowerBound = upperBound) Then
result = 1 'Size equal to 1
End If
End If
End If
NormalExit:
size = result
End Function
Public Function isEmpty( _
ByVal values As Variant _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Boolean
isEmpty = size(values, dimensionOneBased) = 0
End Function
Public Function isDefined( _
ByVal values As Variant _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Boolean
isDefined = size(values, dimensionOneBased) > 0
End Function
警告:
虽然上述“通用”解决方案有效且功能强大,但它并不是性能最高的。 IOW,如果有人知道有人正在使用Dim strings() As String,那么更具体的解决方案可能会快很多倍。
更快的解决方案:
下面的String 的Array 解决方案比上面的“通用解决方案”快很多倍。为什么?因为额外的指令(默认为SIZE_NOT_ARRAY、IsArray、IsEmpty 等)以及从Variant 到Array 的转换似乎带来了可观的成本。在我的测试中,下面的解决方案可以快 10 倍以上。
'Specifically Typed solution for String
Public Const SIZE_EMPTY As Long = 0
'Return Value:
' -1 - Not an Array
' 0 - Empty
' > 0 - Defined
Public Function size( _
ByRef r_values() As String _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Long
Dim result As Long: result = SIZE_EMPTY 'Default to Empty
Dim lowerBound As Long
Dim upperBound As Long
On Error GoTo NormalExit
lowerBound = LBound(r_values, dimensionOneBased) 'Possibly generates error
upperBound = UBound(r_values, dimensionOneBased) 'Possibly generates error
If (lowerBound < upperBound) Then
result = upperBound - lowerBound + 1 'Size greater than 1
Else
If (lowerBound = upperBound) Then
result = 1 'Size equal to 1
End If
End If
NormalExit:
size = result
End Function
Public Function isEmpty( _
ByRef r_values() As String _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Boolean
isEmpty = size(r_values, dimensionOneBased) = 0
End Function
Public Function isDefined( _
ByRef r_values() As String _
, Optional ByVal dimensionOneBased As Long = 1 _
) As Boolean
isDefined = size(r_values, dimensionOneBased) > 0
End Function
【讨论】:
当我们有一个未初始化的动态数组时,UBound 和 LBound 不起作用。
我没有找到解决方案,所以我处理了这个错误。现在它适用于我所有的脚本情况:
Public Function SizeOf(arr As Variant) As Integer
On Error GoTo IsEmpty
SizeOf = UBound(arr) - LBound(arr) + 1
Exit Function
IsEmpty:
SizeOf = 0
End Function
【讨论】:
arr 不是Array,您的解决方案也会返回 0。我也创建了一个可以处理这种情况的答案:stackoverflow.com/a/68614881/501113
在这里和那里编译答案,这里有一套完整的 arr 工具来完成工作:
Function getArraySize(arr As Variant)
' returns array size for a n dimention array
' usage result(k) = size of the k-th dimension
Dim ndims As Long
Dim arrsize() As Variant
ndims = getDimensions(arr)
ReDim arrsize(ndims - 1)
For i = 1 To ndims
arrsize(i - 1) = getDimSize(arr, i)
Next i
getArraySize = arrsize
End Function
Function getDimSize(arr As Variant, dimension As Integer)
' returns size for the given dimension number
getDimSize = UBound(arr, dimension) - LBound(arr, dimension) + 1
End Function
Function getDimensions(arr As Variant) As Long
' returns number of dimension in an array (ex. sheet range = 2 dimensions)
On Error GoTo Err
Dim i As Long
Dim tmp As Long
i = 0
Do While True
i = i + 1
tmp = UBound(arr, i)
Loop
Err:
getDimensions = i - 1
End Function
【讨论】:
试试CountA:
Dim myArray(1 to 10) as String
Dim arrayCount as String
arrayCount = Application.CountA(myArray)
Debug.Print arrayCount
【讨论】:
myArray = Array()
数组长度:
UBound(columns)-LBound(columns)+1
UBound 本身并不是获取每个数组长度的最佳方法,因为 VBA 中的数组可以从不同的索引开始,例如 Dim arr(2 to 10)
UBound 仅当数组从 1 开始时才会返回正确的结果(从 1 开始索引,例如 Dim arr(1 to 10)。在任何其他情况下都会返回错误的结果,例如 Dim arr(10)
更多关于 VBA 阵列的信息 in this VBA Array tutorial.
【讨论】:
功能
Public Function ArrayLen(arr As Variant) As Integer
ArrayLen = UBound(arr) - LBound(arr) + 1
End Function
用法
Dim arr(1 To 3) As String ' Array starting at 1 instead of 0: nightmare fuel
Debug.Print ArrayLen(arr) ' Prints 3. Everything's going to be ok.
【讨论】:
如果变体为空,则会抛出错误。防弹代码如下:
Public Function GetLength(a As Variant) As Integer
If IsEmpty(a) Then
GetLength = 0
Else
GetLength = UBound(a) - LBound(a) + 1
End If
End Function
【讨论】:
columns = Array() 分配一个空数组,则引用的数组不被视为空(实际上不是),函数GetLength 在计算GetLength = UBound(a) - LBound(a) + 1 = -1 -0 +1 = 0 时正确返回零长度。
a As Variant 不是Array 怎么办?这是我为处理所有各种边缘情况而创建的:stackoverflow.com/a/68614881/501113