변수의 전체 유형 결정
변수 의 전체 유형 이란 즉시 창에서 얻을 수있는 정보의 종류를 의미합니다.
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
테스트:
Sub test()
Dim R As Range
Set R = Range("A1")
Debug.Print IsTypeObject(R)
End Sub
그러나 이것은 작업 True
을 만드는 동일한 참조에 의한 전달 의미가 IsVariant
작동해야 한다고 생각할지라도 인쇄 IsTypeObject
합니다 (컬렉션을 범위에 할당 할 수 없습니다). 다양한 조정을 시도했지만 일반 개체 변수와 범위 변수와 같은 특정 개체 변수를 구분할 수없는 것 같습니다.
그래서-전체 유형의 변수를 동적으로 얻는 방법에 대한 아이디어가 있습니까? (동기는 디버그 로그 유틸리티의 일부입니다)
예, 할 수 있습니다. 포인터에 대한 약간의 지식과 'dereferencing'개념이 필요합니다 ...
이를 수행하는 코드는 다음과 같습니다.
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
( CopyMemory
API 함수에 대한 선언 은 몇 단락 아래에 있습니다.)
Visual Basic 언어 제품군은 변수 및 유형의 구현 세부 정보, 특히 포인터 개념으로부터 보호하도록 설계되었으며 내 코드에는 약간의 측면 사고가 포함되어 있기 때문에 설명이 필요합니다.
간단히 말해서 변수에는 이름이 있습니다. 코드에서 볼 수있는 'intX'와 같은 문자열입니다. 실제 데이터를 포함하도록 할당 된 메모리 영역; 그리고 그 메모리에 대한 주소.
이 주소는 실제로 변수에 할당 된 메모리의 시작을위한 것이며, 변수는 데이터의 크기 (또는 길이)와 함께 실제 데이터에 대한 오프셋에 의해 정의 된 메모리의 구조 로 구현 됩니다. , 메모리의 다른 구조에 대한 주소에 대한 오프셋으로. 이러한 크기와 오프셋은 미리 정의되어 있습니다. 변수의 실제 구현이며 VBA 개발자는 이에 대해 거의 알 필요가 없습니다. 유형을 선언하고 모든 작업이 수행됩니다.
오늘 알아야 할 첫 번째 사항은 VBA의 변수 주소에서 처음 두 바이트 가 열거 형 var 유형이라는 것입니다 . 이것이 VarType () 함수가 작동하는 방식입니다.
프로그램이 해당 주소를 전달하면 메모리에 복사 된 데이터 할당을 전달하는 대신 해당 주소를 포인터 로 전달합니다 . 예, 저는이 중 일부를 지나치게 단순화하고 있지만 VBA 개발자는 실제로 포인터를 얻는 것과 데이터 복사본을 얻는 것의 차이점을 알고 있습니다. 함수를 선언 할 때 들어오는 매개 변수에 사용 하는 ByRef
및 ByVal
식별자에 있습니다.
VBA 및 VB는 세부에서 우리를 차폐에 매우 좋다 : 좋은 그래서, 우리가 사용할 수 없다는 VarType
하고 TypeName
우리가 값, 또는에 대한 참조를 통과 한 것을 감지; 또는 참조에 대한 참조, 참조에 대한 참조.
이 문제, 변형은 다른 변수에 대한 래퍼입니다 및 구조를 설명하기 위해 당신에게 그것은 VAR 유형에 포함 된 변수에 대한 포인터를 제공하기 때문에 : 그러나, 우리는 VBA에 있음을 알 방법이 없다 - 우리가 바로 전달을 주소로 표시된 줄 아래로, 우리가 사용할 데이터에 이르기까지 VBA varType
는 포인터로 정의 된 연속 주소를 통해 여러 홉에 의해 간접적으로 거기에 갔다는 것을 결코 알려주지 않습니다.
그러나 API 호출을 사용하여 포인터 뒤에있는 2 바이트를 살펴볼 준비가 되었으면 해당 정보가 존재합니다.
앞서 말했듯 VT_BYREF
이이 두 바이트에는 var 유형이 포함되어 있지만 더 많은 것이 있습니다. 여기에는 데이터 자체가 아니라 저장된 데이터 유형에 대한 참조 또는 포인터임을 나타내는 비트 마커와 결합 된 var 유형이 포함되어 있습니다. 따라서이 코드는 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
언뜻보기에이 함수는 자멸적인 것처럼 보입니다. 변수 (변형 또는 단순 유형)를 참조로 전달하므로 항상
VT_BYREF
. 그리고 어쨌든 '모듈로'산술을 주석 처리했습니다 ...
... 그리고 이것이 실제로 작동하는 방식입니다. 간단한 변수를 전달하면 참조로 변수를 전달했음을 알려줍니다.
Dim str1 As String
str1 = "One Hundred"
Debug.Print "String Variable: " & DereferencedType(str1)
... 그리고 출력을 얻습니다
vbString OR VT_BYREF
.
String Variable: 16392
그러나 함수에 문자열 변형을 전달하면 VBA의 Variant 구현은 포인터에 대한 모든 복잡성과 참조를 통한 전달 ( 데이터까지)으로부터 보호하고 원치 않는 모든 정보가 제거 된 데이터를 제공합니다.
Dim varX As Variant
varX = "One Hundred"
Debug.Print "String Variant: " & DereferencedType(varX)
... 그리고 당신은 출력을 얻습니다 :
String Variant: 8
VT_BYREF
Variant / String 및 Variant / Long 출력의 확장 된 문자열 설명자에 대한 'Variant /'레이블을 제공하기 위해 반환 된 값에 대해 OR 또는 NOT 연산을 코딩하도록 남겨 둘 것 입니다.
[편집 : 해답의 상단에 있으며, 다음과 같이 구현 됨 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
한편, 더 어려운 질문 인 Variant / Object / Range를 얻으려면 추가 작업이 필요합니다. 당신의 변형이 범위를 포함하고 있다는 것을 말할 수 있고, 그 자체가 범위가 아니라 변형이라는 것을 말할 수 있습니다. 범위를 가리 킵니다.
VarX는 범위 개체 변수와 동일하게 설정됩니다. varX : type = 8204 범위 역 참조 된 유형 = 9 rng1 : type = 8204 범위 역 참조 된 유형 = 16393
VarX는 범위 개체의 값인 2 차원 배열과 동일하게 설정됩니다. varX : type = 8204 Variant () 역 참조 된 유형 = 8204 arr1 : type = 8204 Variant () 역 참조 된 유형 = 8204
배열 변수는 Empty ()로 지워집니다. varX 검사 : varX : type = 8204 Variant () 역 참조 된 유형 = 8204 arr1 : type = 8204 Variant () 역 참조 된 유형 = 8204
VarX는 범위로 설정된 '객체'변수와 동일하게 설정됩니다. varX : type = 8204 범위 역 참조 된 유형 = 9 obj1 : type = 8204 범위 역 참조 된 유형 = 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 str1 : type = 8 문자열 역 참조 된 유형 = 16392 lng1 : type = 3 긴 역 참조 된 유형 = 16387
varX 및 str1은 동일한 리터럴로 채워집니다. varX : type = 8 문자열 역 참조 된 유형 = 8 str1 : type = 8 문자열 역 참조 된 유형 = 16392 lng1 : type = 3 긴 역 참조 된 유형 = 16387
varX 및 lng1은 동일한 정수로 채워집니다. varX : type = 2 정수 역 참조 된 유형 = 2 str1 : type = 8 문자열 역 참조 된 유형 = 16392 lng1 : type = 3 긴 역 참조 된 유형 = 16387
VarX는 str1과 동일하게 설정됩니다. varX : type = 8 문자열 역 참조 된 유형 = 8 str1 : type = 8 문자열 역 참조 된 유형 = 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
대체로 흥미로운 질문입니다. 제 대답의 짧은 버전은 변형과 단순 유형을 명확하게 할 수 있지만 '객체'로 선언 된 객체는 해당 분석에 적합하지 않다는 것입니다.
변수가 Variant
이미 있는지 확인하는 코드가 있습니다. 이제해야 할 일은 하위 유형을 가져 오는 것뿐입니다. 정확히이를위한 내장 함수가 있습니다 : VarType .
하지만 한계가 있습니다. 네이티브 유형에서만 작동합니다. vbUserDefinedType
사용자 정의 유형에 대해서는 항상 (36)을 리턴합니다 . 하지만 TypeName
작업을 끝내기 위해 전화를 걸면 특별한 경우가있을 수 있습니다.
참고 URL : https://stackoverflow.com/questions/33941363/determining-the-full-type-of-a-variable
'IT Share you' 카테고리의 다른 글
어떤 상황에서 Apache Spark 대신 Dask를 사용할 수 있습니까? (0) | 2020.12.05 |
---|---|
입력 끝 잭슨 파서로 인해 매핑 할 콘텐츠가 없습니다. (0) | 2020.12.05 |
서비스는 터치 제스처 / 이벤트를 어떻게 수신 할 수 있습니까? (0) | 2020.12.05 |
SQL을 Linq 왼쪽 조인으로 변환 (0) | 2020.12.04 |
Graphviz : 전체 그래프의 글꼴을 변경 하시겠습니까? (0) | 2020.12.04 |