programing

변수의 전체 유형 결정

sourcejob 2023. 4. 19. 22:57
반응형

변수의 전체 유형 결정

변수의 전체 유형이란 바로 다음 창에서 얻을 수 있는 정보를 의미합니다.

여기에 이미지 설명 입력

VBA를 사용하여 타입 정보를 동적으로 결정하고 싶습니다. ★★TypeName()바리안트의 서브타입을 반환하고 범위를 유지하는 바리안트 변수, 범위를 유지하는 오브젝트 변수, 범위를 유지하는 레인지 변수 등을 구분하지 않기 때문에 원하는 대로 동작하지 않습니다.

그 전 단계로 변종이 전달되는지 여부를 검출하는 기능을 작성했습니다.이것은 pass-by-reference 시멘틱스를 이용하여 동작합니다.코드는 변종에서만 수행할 수 있는 인수를 사용하여 작업을 수행하므로 전달된 변수가 실제로 변종이 아닌 경우 오류를 트리거합니다.

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

테스트:

Sub test()
    Dim R As Range
    Set R = Range("A1")
    Debug.Print IsTypeObject(R)
End Sub

은 인쇄되어 있습니다.True 만.IsVariant 또한 일한해 work work work work work work work 로 만들어야 합니다.IsTypeObject일을 하다여러 가지 수정을 시도했지만 일반 객체 변수와 범위 변수와 같은 특정 객체 변수를 구분할 수 없는 것 같습니다.

그럼 어떻게 하면 변수의 완전한 유형을 동적으로 얻을 수 있을까요?(동기는 debug-log 유틸리티의 일부입니다).

네, 이렇게 할 수 있습니다. 포인터와 '디레퍼런스' 개념에 대한 약간의 지식이 필요합니다.

이를 위한 코드는 다음과 같습니다.

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

( (의 선언CopyMemoryAPI를 참조해 주세요.

Visual Basic 언어 패밀리는 변수와 그 유형의 구현 세부사항, 특히 포인터의 개념으로부터 사용자를 보호하도록 설계되어 있기 때문에 설명이 필요합니다.또한 제 코드는 약간 횡적인 사고를 수반합니다.

간단히 말해, 변수에는 이름, 즉 코드에서 볼 수 있는 'intX'와 같은 문자열, 실제 데이터를 포함하도록 할당된 메모리 영역 및 해당 메모리에 대한 주소가 있습니다.

이 주소는 실제로 변수에 할당된 메모리의 시작을 위한 것이며, 변수는 실제 데이터에 대한 오프셋, 데이터의 크기(또는 길이) 및 복잡한 유형의 경우 메모리 내의 다른 구조에 대한 오프셋에 의해 정의된 메모리 내의 구조체로 구현됩니다.이러한 크기와 오프셋은 미리 정의되어 있습니다.변수의 실제 구현이며, VBA 개발자는 이에 대해 거의 알 필요가 없습니다.유형을 선언하면 모든 것이 완료됩니다.

오늘 가장 먼저 알아야 할 것은 VBA 변수 주소의 첫 번째 2바이트는 열거된 변수 유형입니다. VarType() 함수는 이렇게 작동합니다.

프로그램이 해당 주소를 전달하면 메모리에 복사된 데이터 할당을 전달하는 대신 해당 주소를 포인터로 전달합니다.네, 이 중 일부는 지나치게 단순하지만 VBA 개발자들은 실제로 포인터와 데이터 복사본의 차이를 알고 있습니다.ByRef ★★★★★★★★★★★★★★★★★」ByVal함수를 선언할 때 수신 파라미터에 사용하는 식별자입니다.

하는 데수 없을 VarType ★★★★★★★★★★★★★★★★★」TypeName값, 참조 또는 참조에 대한 참조를 전달받았음을 감지합니다.

이는 변종이 다른 변수의 래퍼이기 때문에 이 구조에서 var 타입이 포함된 변수에 대한 포인터를 얻을 수 있기 때문입니다.다만, VBA 에서는, 주소, 사용하는 데이터, 및 VBA 에 의해서 나타나는 행에 직접 건네지는 을 알 수 없습니다.varType포인터에 의해 정의된 연속된 주소를 통해 간접적으로 몇 홉씩 이동했다는 것은 알 수 없습니다.

그러나 API 호출을 사용하여 포인터 뒤에 있는 두 바이트를 볼 준비가 되어 있다면 이 정보는 존재합니다.

이두 바이트에는 var 타입이 그 마커를 한 var 되어 있습니다.VT_BYREF이는 저장된 데이터 유형에 대한 참조 또는 포인터이며 데이터 자체가 아님을 나타냅니다.따라서 이 코드는 VBA를 극복하는 데 도움이 되는 측면적 사고를 통해 VAR 유형을 확실하게 알려줍니다.

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
At a first glance, this function appears to be self-defeating: I'm passing the variable - variant or simple type - by reference, so it's always going to be combined with VT_BYREF. And I've commented-out the 'modulo' arithmetic anyway...

...이렇게 동작합니다.심플한 변수를 전달하면 변수를 참조로 전달했음을 알 수 있습니다.

Dim str1 As String
str1 = "One Hundred"
Debug.Print "String Variable: " & DereferencedType(str1)
...And you get the output vbString OR VT_BYREF:

String Variable: 16392

그러나 문자열 변형 함수를 전달하면 VBA의 Variant 구현으로 포인터와 참조 전달에 대한 복잡함으로부터 데이터를 보호하고 불필요한 정보를 모두 제거할 수 있습니다.

Dim varX As Variant
varX = "One Hundred"
Debug.Print "String Variant:  " & DereferencedType(varX)
...And you get the output:

String Variant:  8

NOT로 코드화하도록 VT_BYREFVariant/String Variant/Long은 Variant/Long이다.

맨에 [: 해 주세요.이 답변은 다음과 같이 구현되어 있습니다.VariantTypeName

다음과 같이 CopyMemory API 호출을 선언하고 발생할 가능성이 있는 모든 환경에 대한 조건부 컴파일러 상수를 지정할 것을 권장합니다.


#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는 VarX를 사용합니다.
varX: == 8204  var유 = 9 : = Type = : 유8 = 8204 범39 = 16393
VarX는 범위 객체의 값인 2차원 배열과 동일하게 설정됩니다. varX: = 8204 Variant() 1= = 8204 :=Variant() Type = : = 8204 Variant() 8 var4 = 8204
empty() () 로로 。 검사 varX 검사: varX: = 8204 Variant() 1= = 8204 :=Variant() Type = : = 8204 Variant() 8 var4 = 8204
VarX는 범위로 설정된 '개체' 변수와 동일하게 설정됩니다. varX: == 8204 j var9 = 9 : = 레인지 =obj1: 유== 8204 범 ob ob16 = 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

결과:

다음 중 하나:
varX: type = 0 빈0 = 0 : =8 String  Type =str1: ==8  str16 = : type =3 Type =  : 유3 = 3 긴16 = 16387
와 에는, 같은 리터럴varX 의 str1 이 . varX: == 8 var8 = 8 : =8 Type =str1: ==8 str16 = : type =3 Type = : 유3 = 3 긴16 = 16387
varX lg1로 . varX: 형= = 2 정22 = 2 : = 8 String Type =str1: ==8 str16 = : type =3 Type = : 유3 = 3 긴16 = 16387
VarX 스트링1 1 var var 。 varX: 유8=8 문18 =8 : String Type = : = 8 문1616 = 16392
VarXLNG1은 VarXLNG1의 LNG입니다. varX: 유3=3 긴유 = 3: type =3 Long Type = : 유3 = 3 긴16 = 16387
VarX의 약점 : = Range Type = 9varX : 유8 = 8204 범9 = 9
VarX는 VarX를 사용합니다. varX: == 8204 var유 = 9 : = Type = : 유8 = 8204 범39 = 16393
VarX는 범위 객체의 값인 2차원 배열과 동일하게 설정됩니다. varX: = 8204 Variant() 1= = 8204 :=Variant() Type = : = 8204 Variant() 8 var4 = 8204
empty() () 로로 。 검사 varX 검사: varX: = 8204 Variant() 1= = 8204 :=Variant() Type = : = 8204 Variant() 8 var4 = 8204
VarX는 범위로 설정된 '개체' 변수와 동일하게 설정됩니다. varX: == 8204 j var9 = 9 : = 레인지 =obj1: 유== 8204 범 ob ob16 = 16393

전반적으로 흥미로운 질문입니다. 제 답변의 짧은 버전은 변종과 단순한 유형을 명확히 할 수 있지만, '객체'로 선언된 개체는 이러한 분석에 응할 수 없다는 것입니다.

가 '아니다'인지 .Variant이미.이제 하위 유형만 구하면 되는 거 맞죠?바로 VarType이라는 기능이 내장되어 있습니다.

그래도 한계가 있어요.네이티브 타입에서만 동작합니다. 반환됩니다.vbUserDefinedType 유형의 (36) "36) "" "" 「 」에의에 의해서, 「 」라고 하는 수 것 같습니다.TypeName일을 끝내기 위해서요

언급URL : https://stackoverflow.com/questions/33941363/determining-the-full-type-of-a-variable

반응형