2016-07-31 84 views
2

我有混淆下面陳述分配多單元格範圍的值給變量

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被聲明爲變體時,代碼如何運行正常,但是當它被聲明爲數組時,會給出錯誤。

+0

爲什麼你希望***能夠分配一個Variant類包含一個2d數組,以便將其分配給一個Integer類型的1d數組? – Comintern

+0

我的不好,x應該被聲明爲一個二維數組,但即使這樣做後,我得到了相同的錯誤 –

+0

爲什麼你希望能夠分配一個Variant'包含一個2d數組可分配給一個** * Integer的2d數組***? – Comintern

回答

1

如果我已經正確理解你的查詢,那麼原因很簡單。

要將數據從工作表傳輸到數組,它需要調整大小。在你的例子中,它是固定的。

試試這個

Dim x() As Variant 

ReDim x(1 To 3, 1 To 3) 

x = Range("A1:C3").Value 

現在爲什麼Variant而不是String?因爲你不知道單元格內容的數據類型是什麼。

3

tl; dr - 編譯器沒有提供足夠的返回類型定義信息來確定在編譯時如何分配內存或如何確定偏移量。

爲了理解這些賦值爲什麼不兼容,有助於理解VBA用來表示每個賦值的基礎數據結構。

.ValueRange的財產返回Variant。存儲在Variant中的類型取決於中的單元格數,即Range。如果有多個單元格,則返回包含Variant數組的Variant。請注意,這是必須返回一個Variant - 否則你需要索引到一個數組中任何時候你想要一個單元格的值。

VBA是一個基於COM的語言,所以當聲明的東西作爲一個Variant,它被存儲在一個COM VARIANT結構,它由一個VARTYPE的描述所包含的數據以及一個指針到基礎數據的(類型前面有聯合中的星號)或數據本身(聯合中沒有星號的類型)。在內存方面,它看起來像這樣:

Variant in memory

所以,當你使用賦值x = Range("A1:C3").Value,你描述的Variant陣列的VARTYPE。這很重要,你會在下面看到。

如果Range只有一個單元格,你還可以得到一個Variant,但它包含的基本類型 - 數組。

當你在VBA中聲明一個數組時,它存儲在一個COM SAFEARRAY結構中,該結構描述數組的方式可以讓其他COM客戶端使用該數組。在內存方面,它看起來像這樣(注意,這是一個一個維數組 - 在最後的SAFEARRAYBOUND實際上與元素的CDIM數的數組):

SafeArray in memory

這基本上就是你得到聲明Dim x(1 To 3, 1 To 3) As Integer(除了最後會有2個SAFEARRAYBOUNDs)。

請注意,這兩種數據類型之間有兩個非常重要的區別。鬆散類型的聲明Dim x As Variant允許運行時確定數據區中包含的內容。在Range.Value賦值的情況下,您將獲得一個兼容類型的Variant數組的VARTYPE(這也是爲什麼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可以保護您免受訪問衝突和/或運行時錯誤轉換。