【问题标题】:Get length of array?获取数组的长度?
【发布时间】:2015-08-14 23:45:59
【问题描述】:

我正在尝试获取数组的长度,但我不断收到此错误:

需要对象

我做错了吗?

Dim columns As Variant
columns = Array( _
"A", "ID", _
"D", "Name")
Debug.Print columns.Length  ' Error: Object required

【问题讨论】:

标签: excel vba ms-access


【解决方案1】:

复制/粘贴解决方案:
最常见的答案是这样的:

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,那么更具体的解决方案可能会快很多倍。


更快的解决方案:
下面的StringArray 解决方案比上面的“通用解决方案”快很多倍。为什么?因为额外的指令(默认为SIZE_NOT_ARRAYIsArrayIsEmpty 等)以及从VariantArray 的转换似乎带来了可观的成本。在我的测试中,下面的解决方案可以快 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

【讨论】:

    【解决方案2】:

    当我们有一个未初始化的动态数组时,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
    

    【讨论】:

    【解决方案3】:

    在这里和那里编译答案,这里有一套完整的 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
    

    【讨论】:

      【解决方案4】:

      试试CountA:

      Dim myArray(1 to 10) as String
      Dim arrayCount as String
      arrayCount = Application.CountA(myArray)
      Debug.Print arrayCount
      

      【讨论】:

      • 好主意;注意事项:a) 如果只有声明而没有任何实际赋值,则返回 1,b) 如果为空,则返回 1,如 myArray = Array()
      • 这不应该是 WorksheetFunction.CountA 吗?
      • @hohny,在我的 POC 中,Application.CountA 有效。
      【解决方案5】:

      数组长度:

      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.

      【讨论】:

      • 真的吗?像数组长度这样基本的东西没有本机功能吗?哇。这似乎很麻烦,不得不使用 Len(columns) 的替代方法。
      • @James 那么长度属性会返回什么? VBA 数组最多可以有 60 个维度……最常见的场景是 2D 数组。那么,Length 会返回什么?
      • @excel hero - 我想你会让它像 UBound(ArrayName, [Dimension]) 一样工作,不是吗?
      • @ExcelHero 大多数语言只返回第一个维度,然后如果调用第一个维度的子索引,则可以返回第二个维度的长度,依此类推。
      • 只有在数组已定义且非空的情况下,答案代码才是正确的。如果您真的想一般地获取数组的大小,那么这是一个处理所有边缘情况的强大解决方案:stackoverflow.com/a/68614881/501113
      【解决方案6】:

      功能

      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.
      

      【讨论】:

      • 只有在数组已定义且非空的情况下,答案代码才是正确的。如果您真的想一般地获取数组的大小,那么这是一个处理所有边缘情况的强大解决方案:stackoverflow.com/a/68614881/501113
      【解决方案7】:

      如果变体为空,则会抛出错误。防弹代码如下:

      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
      

      【讨论】:

      • 一个很好的补充 +1:-) - 只是一个旁注:如果通过 columns = Array() 分配一个空数组,则引用的数组不被视为空(实际上不是),函数GetLength 在计算GetLength = UBound(a) - LBound(a) + 1 = -1 -0 +1 = 0 时正确返回零长度。
      • 这是一种比已接受答案中的解决方案更强大的解决方案。然而,作为一个极端情况,如果a As Variant 不是Array 怎么办?这是我为处理所有各种边缘情况而创建的:stackoverflow.com/a/68614881/501113
      猜你喜欢
      • 2015-08-05
      • 2013-10-02
      • 1970-01-01
      • 2015-05-11
      • 2011-11-14
      • 1970-01-01
      • 1970-01-01
      • 2013-12-19
      • 2023-04-06
      相关资源
      最近更新 更多