[英]Assigning multi cell range values to a variable
我對以下陳述感到困惑
Dim x as variant
x=Range("A1:C3").value
使用上面的語句后,我們可以將x用作二維數組,但是如果我們將x聲明為如下數組
Dim x(1 to 3,1 to 3) as integer
x=Range("A1:C3").value
然后使用上面的statemnet給我編譯時錯誤,指出Can't assign to an array
。
我的疑問是,將x聲明為variant時代碼如何正常運行,但是將其聲明為數組時卻給我錯誤。
tl; dr-返回類型定義沒有為編譯器提供足夠的信息來確定如何分配內存或如何確定在編譯時進行轉換的偏移量。
為了理解為什么這些分配不兼容,它有助於理解VBA用於表示每個分配的基礎數據結構。
Range
的.Value
屬性返回Variant
。 存儲在類型Variant
是由在小區的數目來確定Range
。 如果有多於一個的小區時,它返回一個Variant
包含的陣列Variant
。 請注意, 必須返回Variant
否則,只要您需要單個單元格中的值,就需要索引到數組中。
VBA是一種基於COM的語言,因此,當您將某些內容聲明為Variant
,它將存儲在COM VARIANT結構中,該結構由VARTYPE組成,該VARTYPE描述所包含的數據以及指向基礎數據的指針(類型中以星號開頭的類型)聯合)或數據本身(聯合中未加星號的類型)。 在內存中,它看起來像這樣:
因此,當您使用賦值x = Range("A1:C3").Value
,您將獲得一個描述了Variant
數組的VARTYPE。 這很重要,如下所示。
如果Range
僅具有一個單元格,則您還將獲得Variant
,但它包含基礎類型- 而不是數組。
在VBA中聲明數組時,該數組存儲在COM SAFEARRAY結構中,該結構以使該數組可被其他COM客戶端使用的方式描述該數組。 在內存中看起來像這樣(請注意,這是一個一維數組-SAFEARRAYBOUND實際上是一個數組,其中包含cDim中的元素數):
這基本上就是用聲明Dim x(1 To 3, 1 To 3) As Integer
(除了最后會有2個SAFEARRAYBOUNDs)。
請注意,這兩種數據類型之間有2個非常重要的區別。 寬松類型的聲明Dim x As Variant
允許運行時確定數據區域中包含的內容。 對於Range.Value
賦值,您將獲得一個Variant
類型為Variant
的數組,這是一個兼容類型(這也是為什么Dim x() As Variant
會編譯的原因)。 聲明Dim y(1 To 3, 1 To 3) As Integer
在編譯時固定 。 更重要的是,由於內存中SAFEARRAY結構的大小由維數決定,因此編譯器可以在編譯時分配內存。 但是,不能為COM調用返回的任意SAFEARRAY結構分配的內存量。 此外,指向的存儲區域的大小由所包含類型的字節長度和元素總數決定。 編譯器通過禁止分配來防止不匹配的可能性。
實際上,這很可能是您無法直接獲取指向SAFEARRAY的指針的原因(唯一的方法是將其轉換為Variant
並從其數據區域手動取消引用該指針):
Dim x(1 To 3, 1 To 3) As Integer
Debug.Print VarPtr(x) '<- Type mismatch.
因此,將其分解,您將無法執行此操作,因為編譯器沒有足夠的信息來安全地進行運行時強制轉換。 如果您想在后台進行一些戳操作,則以下代碼演示了正在發生的情況:
Public Declare Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (Destination As Any, Source As Any, _
ByVal length As Long)
Public Type ComVariant
VarType As Integer
Reserved1 As Integer
Reserved2 As Integer
Reserved3 As Integer
DataArea As Long
End Type
Public Sub ExamineVariables()
Dim x As Variant
x = Range("A1:C3").Value
Dim testV As ComVariant
CopyMemory testV, x, LenB(testV)
Debug.Print testV.VarType '= 8204 = 0x200C = VT_ARRAY & VT_VARIANT
Debug.Print testV.DataArea 'Varies - is a SafeArray pointer.
Dim y(1 To 3, 1 To 3) As Integer
View2dArrayType y
End Sub
Public Sub View2dArrayType(vbArray As Variant)
Dim testV As ComVariant
'The VT_BYREF can be ignored - it is an artifact of the cast to Variant.
CopyMemory testV, vbArray, LenB(testV)
Debug.Print testV.VarType '= 24578 = 0x6002 = VT_ARRAY & VT_BYREF & VT_I2
End Sub
您的第一個聲明是Variant
數組,其中每個元素長12個字節。 您的第二個聲明是一個Integer
數組,每個元素的長度為2個字節。 在編譯時,不能簡單地確定返回的存儲區的長度或適當的轉換。 VBA正在保護您免受訪問沖突和/或運行時錯誤強制轉換。
如果我正確理解了您的查詢,則原因非常簡單。
要將數據從工作表傳輸到數組,需要調整大小。 在您的示例中,它是固定的。
嘗試這個
Dim x() As Variant
ReDim x(1 To 3, 1 To 3)
x = Range("A1:C3").Value
現在為什么要使用Variant
而不是String
? 因為您不知道單元格內容的數據類型是什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.