Vba 确定变量的完整类型

Vba 确定变量的完整类型,vba,excel,Vba,Excel,我所说的变量的完整类型是指在即时窗口中获得的信息类型: 我想使用VBA动态确定类型信息。函数TypeName()没有实现我想要的功能,因为它返回变量的子类型,并且不区分持有范围的变量、持有范围的对象变量和持有范围的范围变量 作为初步步骤,我编写了一个函数,用于检测是否向其传递了变量。它通过利用引用传递语义来工作。代码使用其参数执行操作,这些操作只能使用变量执行,因此,如果传递的变量实际上不是变量,则会触发错误: Function IsVariant(var As Variant) As Boo

我所说的变量的完整类型是指在即时窗口中获得的信息类型:

我想使用VBA动态确定类型信息。函数
TypeName()
没有实现我想要的功能,因为它返回变量的子类型,并且不区分持有范围的变量、持有范围的对象变量和持有范围的范围变量

作为初步步骤,我编写了一个函数,用于检测是否向其传递了变量。它通过利用引用传递语义来工作。代码使用其参数执行操作,这些操作只能使用变量执行,因此,如果传递的变量实际上不是变量,则会触发错误:

Function IsVariant(var As Variant) As Boolean
    Dim temp As Variant
    Dim isVar As Boolean

    If IsObject(var) Then
        Set temp = var
    Else
        temp = var
    End If

    On Error Resume Next
        Set var = New Collection
        var = "test"
        If Err.Number > 0 Then
            isVar = False
        Else
            isVar = True
        End If
    On Error GoTo 0

    If IsObject(temp) Then
        Set var = temp
    Else
        var = temp
    End If
    IsVariant = isVar
End Function
基于此,我写道:

Function FullType(var As Variant) As String
    If IsVariant(var) Then
        FullType = "Variant/" & TypeName(var)
    Else
        FullType = TypeName(var)
    End If
End Function
测试代码:

Sub TestTypes()
    Dim R As Range
    Dim Ob As Object
    Dim i As Integer
    Dim v1 As Variant
    Dim v2 As Variant

    v1 = 10
    i = 10

    Set v2 = Range("A1")
    Set Ob = Range("A2")
    Set R = Range("A3")

    Debug.Print "v1: " & FullType(v1)
    Debug.Print "i: " & FullType(i)
    Debug.Print "v2: " & FullType(v2)
    Debug.Print "Ob: " & FullType(Ob)
    Debug.Print "R: " & FullType(R)  
End Sub
输出:

v1: Variant/Integer
i: Integer
v2: Variant/Range
Ob: Range
R: Range
这几乎就是我想要的——但不区分包含范围的对象变量和包含范围的范围变量。我曾尝试编写一个名为
IsTypeObject
的函数,其工作原理类似于
IsVariant
,但似乎无法使其工作:

Function IsTypeObject(var As Variant) As Boolean
    Dim temp As Variant
    Dim isGeneric As Boolean

    If (Not IsObject(var)) Or IsVariant(var) Then
        IsTypeObject = False
        Exit Function
    End If

    Set temp = var
    On Error Resume Next
        Set var = New Collection
        Set var = ActiveWorkbook
        If Err.Number > 0 Then
            isGeneric = False
        Else
            isGeneric = True
        End If
    On Error GoTo 0

    Set var = temp
    IsTypeObject = isGeneric
End Function
测试:

但这会打印出
True
,即使我认为使
IsVariant
起作用的相同的传递引用语义也会使
IsTypeObject
起作用(您不能将集合分配给范围)。我尝试了各种调整,但似乎无法区分通用对象变量和特定对象变量,如范围变量


那么——关于如何动态获取变量的完整类型,有什么想法吗?(动机是作为调试日志实用程序的一部分)

您拥有确定变量是否已经是变量的代码。现在你需要做的就是得到子类型,对吗?有一个内置函数正好用于:


但它也有局限性。它只适用于本机类型。对于用户定义的类型,它总是返回
vbUserDefinedType
(36)。尽管如此,我想您可以通过调用
TypeName
来完成这项工作

是的,你可以这样做:它需要对指针和“去引用”的概念有一点了解

下面是执行此操作的代码: 乍一看,这个函数似乎会弄巧成拙:我通过引用传递变量-变量或简单类型,因此它总是与VT\u BYREF组合。我已经注释掉了“模”运算

…这就是它的实际工作原理:向它传递一个简单的变量,它会告诉您通过引用传递了该变量: …然后得到输出vbString或VT_BYREF

但是,如果您将字符串变量传递给我们的函数,VBA对该变量的实现将使您免于指针和引用传递的复杂性-一直传递到数据-,并为您的数据去除所有不需要的信息: …然后得到输出:

我将让您使用
VT_BYREF
对返回的值编写一个OR OR NOT操作,为变量/字符串和变量/长输出的扩展字符串描述符提供“Variant/”标签

[编辑:这样做了,它位于答案的顶部,实现为VariantTypeName]

我建议您如图所示声明CopyMemory API调用,并为您可能遇到的所有环境使用条件编译器常量: #如果是VBA7和Win64,则“64位Windows下的64位Excel” '使用LongLong和LongPtr
私有声明PtrSafe子CopyMemory Lib“kernel32”别名“rtlmovemory”_ (如有目的地_ 任何来源_ ByVal长度为LongLong)
#ElseIf VBA7然后在所有环境中使用64位Excel '仅使用LongPtr,LongLong不可用
私有声明PtrSafe子CopyMemory Lib“kernel32”别名“rtlmovemory”_ (如有目的地_ 任何来源_ ByVal长度与长度相同)
#Else'32位Excel
私有声明子CopyMemory Lib“kernel32”别名“rtlmovemory”_ (如有目的地_ 任何来源_ ByVal长度(与长度相同) #如果结束

与此同时,更难的问题——获取变体/对象/范围——还需要进一步研究。我可以告诉你,你的变量包含一个范围,我可以告诉你它是一个变量,而不是它本身是一个范围:但是我不能沿着声明链往下说,既然一个对象指向一个范围,那么它就声明为“object”:

VarX设置为等于范围对象变量: varX:type=8204范围解除引用类型=9 rng1:type=8204范围解除引用的type=16393
VarX设置为等于范围对象的值,即二维数组: varX:type=8204 Variant()取消引用的类型=8204 arr1:type=8204 Variant()取消引用的type=8204
数组变量被擦除为空()。检查varX: varX:type=8204 Variant()取消引用的类型=8204 arr1:type=8204 Variant()取消引用的type=8204
VarX设置为等于“对象”变量,该变量已设置为以下范围: varX:type=8204范围解除引用类型=9 obj1:类型=8204范围解除引用类型=16393

以下是生成该代码的代码和完整输出:

公共子TestVar()
Dim-varX作为变体 将str1设置为字符串 将lng1调暗至最长 将rng1设置为Excel.Range Dim-arr1作为变体 作为对象的尺寸obj1
调试。打印“未初始化”: 调试。打印 Debug.Print vbTab&“varX:type=”&VarType(varX)&vbTab&vbTab&TypeName(varX)
Sub test()
    Dim R As Range
    Set R = Range("A1")
    Debug.Print IsTypeObject(R)
End Sub
Public Function VariantTypeName(ByRef MyVariant) As String ' Returns the expanded type name of a variable, indicating ' whether it's a simple data type (eg: Long Integer), or a ' Variant containing data of that type, eg: "Variant/Long"
Dim iType As Integer Const VT_BYREF = &H4000&
CopyMemory iType, MyVariant, 2
' iType now contains the VarType of the incoming parameter ' combined with a bitwise VT_BYREF flag indicating that it ' was passed by reference. In other words, it's a pointer, ' not the data structure of the variable (or a copy of it)
' So we should have VT_BYREF - and we'd always expect to ' when MyVariant is a Variant, as variants are a structure ' which uses a pointer (or pointers) to the stored data...
' However, the VBA implementation of a variant will always ' dereference the pointer - all the pointers - passing us ' straight to the data, stripping out all that information ' about references...
If (iType And VT_BYREF) = VT_BYREF Then ' Bitwise arithmetic detects the VT_BYREF flag: VariantTypeName = TypeName(MyVariant) Else ' No VT_BYREF flag. This is a Variant, not a variable: VariantTypeName = "Variant/" & TypeName(MyVariant) End If
End Function
Public Function DereferencedType(ByRef MyVar) As Long
Dim iType As Integer
Const VT_BYREF = &H4000&
' The first two bytes of a variable are the type ID with a ' bitwise OR to VT_BYREF if we were passed the variable by ' reference... Which is exactly what this function does:
CopyMemory iType, MyVar, 2
DereferencedType = iType ' Mod VT_BYREF
'Use "Mod VT_BYREF" to separate out the type if you want
End Function
Dim str1 As String str1 = "One Hundred" Debug.Print "String Variable: " & DereferencedType(str1) String Variable: 16392 Dim varX As Variant varX = "One Hundred" Debug.Print "String Variant: " & DereferencedType(varX) String Variant: 8 #If VBA7 And Win64 Then ' 64 bit Excel under 64-bit Windows ' Use LongLong and LongPtr
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, _ Source As Any, _ ByVal Length As LongLong)
#ElseIf VBA7 Then ' 64 bit Excel in all environments ' Use LongPtr only, LongLong is not available
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, _ Source As Any, _ ByVal Length As Long)
#Else ' 32 bit Excel
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, _ Source As Any, _ ByVal Length As Long) #End If
VarX is set equal to a range object variable: varX: type=8204 Range Dereferenced Type=9 rng1: type=8204 Range Dereferenced Type=16393
VarX is set equal to a range object's value, a 2-dimensional array: varX: type=8204 Variant() Dereferenced Type=8204 arr1: type=8204 Variant() Dereferenced Type=8204
The array variable is erased to Empty(). Inspect varX: varX: type=8204 Variant() Dereferenced Type=8204 arr1: type=8204 Variant() Dereferenced Type=8204
VarX is set equal to an 'object' variable, which has been set to a range: varX: type=8204 Range Dereferenced Type=9 obj1: type=8204 Range Dereferenced Type=16393 Public Sub TestVar()
Dim varX As Variant Dim str1 As String Dim lng1 As Long Dim rng1 As Excel.Range Dim arr1 As Variant Dim obj1 As Object
Debug.Print "Uninitialised:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "str1: type=" & VarType(str1) & vbTab & vbTab & TypeName(str1) & vbTab & "Dereferenced Type=" & DereferencedType(str1) Debug.Print vbTab & "lng1: type=" & VarType(lng1) & vbTab & vbTab & TypeName(lng1) & vbTab & "Dereferenced Type=" & DereferencedType(lng1) Debug.Print
varX = "One Hundred" str1 = "One Hundred" lng1 = 100 Debug.Print "varX and str1 are populated with the same literal:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "str1: type=" & VarType(str1) & vbTab & vbTab & TypeName(str1) & vbTab & "Dereferenced Type=" & DereferencedType(str1) Debug.Print vbTab & "lng1: type=" & VarType(lng1) & vbTab & vbTab & TypeName(lng1) & vbTab & "Dereferenced Type=" & DereferencedType(lng1) Debug.Print
varX = 100 lng1 = 100 Debug.Print "varX and lng1 are populated with the same integer:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "str1: type=" & VarType(str1) & vbTab & vbTab & TypeName(str1) & vbTab & "Dereferenced Type=" & DereferencedType(str1) Debug.Print vbTab & "lng1: type=" & VarType(lng1) & vbTab & vbTab & TypeName(lng1) & vbTab & "Dereferenced Type=" & DereferencedType(lng1) Debug.Print
varX = str1 Debug.Print "VarX is set equal to str1:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "str1: type=" & VarType(str1) & vbTab & vbTab & TypeName(str1) & vbTab & "Dereferenced Type=" & DereferencedType(str1) Debug.Print
varX = lng1 Debug.Print "VarX is set equal to lng1:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "lng1: type=" & VarType(lng1) & vbTab & vbTab & TypeName(lng1) & vbTab & "Dereferenced Type=" & DereferencedType(lng1) Debug.Print
Set varX = ActiveSheet.Range("A1:C3") Debug.Print "VarX is set equal to a range:" Debug.Print Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print
Set rng1 = ActiveSheet.Range("A1:C3") Set varX = Nothing Set varX = rng1 Debug.Print "VarX is set equal to a range object variable:" Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "rng1: type=" & VarType(rng1) & vbTab & vbTab & TypeName(rng1) & vbTab & "Dereferenced Type=" & DereferencedType(rng1) Debug.Print
arr1 = rng1.Value2 Set varX = Nothing varX = arr1 Debug.Print "VarX is set equal to a range object's value, a 2-dimensional array:" Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "arr1: type=" & VarType(rng1) & vbTab & vbTab & TypeName(arr1) & vbTab & "Dereferenced Type=" & DereferencedType(arr1) Debug.Print
Erase arr1 Debug.Print "The array variable is erased to Empty(). Inspect varX:" Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "arr1: type=" & VarType(rng1) & vbTab & vbTab & TypeName(arr1) & vbTab & "Dereferenced Type=" & DereferencedType(arr1) Debug.Print
Set obj1 = ActiveSheet.Range("A1:C3") Set varX = Nothing Set varX = obj1 Debug.Print "VarX is set equal to an 'object' variable, which has been set to a range:" Debug.Print vbTab & "varX: type=" & VarType(varX) & vbTab & vbTab & TypeName(varX) & vbTab & "Dereferenced Type=" & DereferencedType(varX) Debug.Print vbTab & "obj1: type=" & VarType(rng1) & vbTab & vbTab & TypeName(obj1) & vbTab & "Dereferenced Type=" & DereferencedType(obj1) Debug.Print
End Sub
Uninitialised: varX: type=0 Empty Dereferenced Type=0 str1: type=8 String Dereferenced Type=16392 lng1: type=3 Long Dereferenced Type=16387
varX and str1 are populated with the same literal: varX: type=8 String Dereferenced Type=8 str1: type=8 String Dereferenced Type=16392 lng1: type=3 Long Dereferenced Type=16387
varX and lng1 are populated with the same integer: varX: type=2 Integer Dereferenced Type=2 str1: type=8 String Dereferenced Type=16392 lng1: type=3 Long Dereferenced Type=16387
VarX is set equal to str1: varX: type=8 String Dereferenced Type=8 str1: type=8 String Dereferenced Type=16392
VarX is set equal to lng1: varX: type=3 Long Dereferenced Type=3 lng1: type=3 Long Dereferenced Type=16387
VarX is set equal to a range: varX: type=8204 Range Dereferenced Type=9
VarX is set equal to a range object variable: varX: type=8204 Range Dereferenced Type=9 rng1: type=8204 Range Dereferenced Type=16393
VarX is set equal to a range object's value, a 2-dimensional array: varX: type=8204 Variant() Dereferenced Type=8204 arr1: type=8204 Variant() Dereferenced Type=8204
The array variable is erased to Empty(). Inspect varX: varX: type=8204 Variant() Dereferenced Type=8204 arr1: type=8204 Variant() Dereferenced Type=8204
VarX is set equal to an 'object' variable, which has been set to a range: varX: type=8204 Range Dereferenced Type=9 obj1: type=8204 Range Dereferenced Type=16393