簡體   English   中英

將多單元格范圍值分配給變量

[英]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中的元素數):

內存中的SafeArray

這基本上就是用聲明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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM