如何确定在VB6中数组是否已初始化?

63

在VB6中,如果向未定义维数的数组传递到Ubound函数会导致错误,因此在尝试检查其上限之前,我希望检查它是否已被定义维度。如何实现这一点?

24个回答

1
最简单的处理方式是确保在需要检查Ubound之前,数组已经被初始化。我需要一个在表单代码的(General)区域声明的数组。 例如:
Dim arySomeArray() As sometype

然后在表单加载程序中重新定义了数组:
Private Sub Form_Load()

ReDim arySomeArray(1) As sometype 'insure that the array is initialized

End Sub 

这将允许数组在程序的任何时候被重新定义。当您确定数组需要多大时,只需使用redim。

ReDim arySomeArray(i) As sometype 'i is the size needed to hold the new data

1
根据我阅读的所有信息,对于一个未初始化的类型化数组,以下内容对我最有帮助:

它使得测试代码与UBOUND的使用保持一致,并且不需要使用错误处理进行测试。

它依赖于零基数组(这在大多数开发中都是如此)。

不能使用“Erase”来清除数组。请使用下面列出的替代方法。

Dim data() as string ' creates the untestable holder.
data = Split(vbNullString, ",") ' causes array to return ubound(data) = -1
If Ubound(data)=-1 then ' has no contents
    ' do something
End If
redim preserve data(Ubound(data)+1) ' works to increase array size regardless of it being empty or not.

data = Split(vbNullString, ",") ' MUST use this to clear the array again.

1
题目的标题问如何确定数组是否初始化,但是阅读问题后,看起来真正的问题是如何获取未初始化的数组的UBound

这是我的解决方案(针对实际问题,而不是标题):

Function UBound2(Arr) As Integer
  On Error Resume Next
  UBound2 = UBound(Arr)
  If Err.Number = 9 Then UBound2 = -1
  On Error GoTo 0
End Function

这个函数在以下四种情况下起作用,前三种是当Arr由外部dll COM创建时发现的,第四种是当Arr没有进行ReDim(本问题的主题):

  • UBound(Arr)有效,因此调用UBound2(Arr)会增加一些开销,但不会对性能造成太大影响
  • UBound(Arr)在定义Arr的函数中失败,但在UBound2()内成功
  • UBound(Arr)在定义Arr的函数和UBound2()中都失败,因此错误处理完成任务
  • Dim Arr() As Whatever之后,在ReDim Arr(X)之前

1

对于任何声明为数组的变量,您可以通过调用SafeArrayGetDim API轻松检查数组是否已初始化。如果数组已初始化,则返回值将为非零,否则函数返回零。

请注意,您不能使用此函数处理包含数组的变体。这样做会导致编译错误(类型不匹配)。

Public Declare Function SafeArrayGetDim Lib "oleaut32.dll" (psa() As Any) As Long

Public Sub Main()
    Dim MyArray() As String

    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(64)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Erase MyArray
    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(31, 15, 63)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Erase MyArray
    Debug.Print SafeArrayGetDim(MyArray)    ' zero

    ReDim MyArray(127)
    Debug.Print SafeArrayGetDim(MyArray)    ' non-zero

    Dim vArray As Variant
    vArray = MyArray
    ' If you uncomment the next line, the program won't compile or run.
    'Debug.Print SafeArrayGetDim(vArray)     ' <- Type mismatch
End Sub

我遇到了编译错误常量、定长字符串、数组、用户定义类型和Declare语句不允许作为对象模块的公共成员。然而,我将函数声明更改为“Private”,它就可以工作了。 - Bert
"Sub Main" 应该已经表明示例代码应该在模块中而不是类中。 :) 在模块中声明函数为公共函数后,您可以从项目中的任何位置调用它。 - Scruff
SafeArrayGetDim将返回存储在MyArray变量中的SAFEARRAY*指针的低DWORD。这个低DWORD对于空指针(未分配的数组)将为0,但对于有效指针(如&H12340000)可能也是0,这是一个已分配数组的非空指针,因此这种方法不是非常可靠的。 - wqw

0

您可以使用Ubound()函数解决此问题,通过使用JScript的VBArray()对象检查数组是否为空(适用于变体类型、单维或多维数组),以获取元素总数来确定:

Sub Test()

    Dim a() As Variant
    Dim b As Variant
    Dim c As Long

    ' Uninitialized array of variant
    ' MsgBox UBound(a) ' gives 'Subscript out of range' error
    MsgBox GetElementsCount(a) ' 0

    ' Variant containing an empty array
    b = Array()
    MsgBox GetElementsCount(b) ' 0

    ' Any other types, eg Long or not Variant type arrays
    MsgBox GetElementsCount(c) ' -1

End Sub

Function GetElementsCount(aSample) As Long

    Static oHtmlfile As Object ' instantiate once

    If oHtmlfile Is Nothing Then
        Set oHtmlfile = CreateObject("htmlfile")
        oHtmlfile.parentWindow.execScript ("function arrlength(arr) {try {return (new VBArray(arr)).toArray().length} catch(e) {return -1}}"), "jscript"
    End If
    GetElementsCount = oHtmlfile.parentWindow.arrlength(aSample)

End Function

对我来说,每个元素大约需要0.4毫秒+100毫秒的初始化时间,使用VB 6.0.9782编译。因此,10M元素的数组大约需要4.1秒。相同的功能可以通过ScriptControl ActiveX实现。

0
If ChkArray(MyArray)=True then
   ....
End If

Public Function ChkArray(ByRef b) As Boolean
    On Error goto 1
    If UBound(b) > 0 Then ChkArray = True
End Function

0

有两个稍微不同的测试场景:

  1. 数组被初始化(实际上它不是一个空指针)
  2. 数组被初始化,并且至少有一个元素

Case 2对于像 Split(vbNullString, ",") 这样返回 LBound=0UBound=-1String 数组是必需的。 以下是我为每个测试生成的最简单的示例代码片段:

Public Function IsInitialised(arr() As String) As Boolean
  On Error Resume Next
  IsInitialised = UBound(arr) <> 0.5
End Function

Public Function IsInitialisedAndHasElements(arr() As String) As Boolean
  On Error Resume Next
  IsInitialisedAndHasElements = UBound(arr) >= LBound(arr)
End Function

0
如果数组是字符串数组,您可以使用Join()方法进行测试:
Private Sub Test()

    Dim ArrayToTest() As String

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "false"

    ReDim ArrayToTest(1 To 10)

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "true"

    ReDim ArrayToTest(0 To 0)

    MsgBox StringArrayCheck(ArrayToTest)     ' returns "false"

End Sub


Function StringArrayCheck(o As Variant) As Boolean

    Dim x As String

    x = Join(o)

    StringArrayCheck = (Len(x) <> 0)

End Function

+1 是目前最简单的检测空字符串数组的方法。 - Hannah Vernon

0

我的唯一问题是从32位移植到64位操作系统的API调用。
这适用于对象、字符串等...

Public Function ArrayIsInitialized(ByRef arr As Variant) As Boolean
    On Error Resume Next
    ArrayIsInitialized = False
    If UBound(arr) >= 0 Then If Err.Number = 0 Then ArrayIsInitialized = True
End Function

2
在发表自己的答案之前,您应该仔细阅读现有的答案。我在四年前提出了这个解决方案。 - raven

0
' Function CountElements return counted elements of an array.
' Returns:
' [               -1]. If the argument is not an array.
' [                0]. If the argument is a not initialized array.
' [Count of elements]. If the argument is an initialized array.
Private Function CountElements(ByRef vArray As Variant) As Integer

    ' Check whether the argument is an array.
    If (VarType(vArray) And vbArray) <> vbArray Then
        
        ' Not an array.                 CountElements is set to -1.
        Let CountElements = -1
        
    Else
    
        On Error Resume Next
        
        ' Calculate number of elements in array.
        ' Scenarios:
        ' - Array is initialized.       CountElements is set to counted elements.
        ' - Array is NOT initialized.   CountElements is never set and keeps its
        '                               initial value of zero (since an error is
        '                               raised).
        Let CountElements = (UBound(vArray) - LBound(vArray)) + 1
    
    End If
    
End Function


' Test of function CountElements.

    Dim arrStr() As String
    Dim arrV As Variant
    
    Let iCount = CountElements(arrStr)                  ' arrStr is not initialized, returns 0.
    ReDim arrStr(2)
    Let iCount = CountElements(arrStr)                  ' arrStr is initialized, returns 3.
    ReDim arrStr(5 To 8)
    Let iCount = CountElements(arrStr)                  ' arrStr is initialized, returns 4.
    Let arrV = arrStr
    Let iCount = CountElements(arrV)                    ' arrV contains a boxed arrStr which is initialized, returns 4
    Erase arrStr
    Let iCount = CountElements(arrStr)                  ' arrStr size is erased, returns 0.
    
    Let iCount = CountElements(Nothing)                 ' Nothing is not an array, returns -1.
    Let iCount = CountElements(Null)                    ' Null is not an array, returns -1.
    Let iCount = CountElements(5)                       ' Figure is not an array, returns -1.
    Let iCount = CountElements("My imaginary array")    ' Text is not an array, returns -1.
    Let iCount = CountElements(Array(1, 2, 3, 4, 5))    ' Created array of Integer elements, returns 5.
    Let iCount = CountElements(Array("A", "B", "C"))    ' Created array of String elements, returns 3.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接