我对以下陈述感到困惑
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 :(得分:3)
tl; dr - 编译器的返回类型定义没有提供足够的信息来确定如何分配内存或如何在编译时确定转换的偏移量。
为了理解为什么这些赋值不兼容,有助于理解VBA用于表示每个赋值的基础数据结构。
.Value
的{{1}}属性返回Range
。存储在Variant
中的类型由Variant
中 中的单元格数决定。如果有多个单元格,则会返回包含Range
数组的Variant
。请注意, 必须 返回Variant
- 否则,只要您想要来自单个单元格的值,就需要索引到数组中。
VBA是一种基于COM的语言,因此当您将某些内容声明为Variant
时,它会存储在由VARIANT组成的COM VARTYPE结构中,该结构描述所包含的数据和指向基础数据的指针(联合中带有星号的类型)或数据本身(联合中不带星号的类型)。在内存中,它看起来像这样:
因此,当您使用作业Variant
时,您将获得一个描述x = Range("A1:C3").Value
数组的VARTYPE。这很重要,如下所示。
如果Variant
只有一个单元格,那么您还会获得Range
,但它包含基础类型 - 不 数组。< / p>
在VBA中声明一个数组时,它存储在一个COM SAFEARRAY结构中,该结构以一种使其可供其他COM客户端使用的方式来描述该数组。在内存中它看起来像这样(请注意,这是一维数组 - 最后的SAFEARRAYBOUND实际上是一个包含cDim中元素数量的数组):
这基本上就是你使用声明Variant
得到的结果(除了最后会有2个SAFEARRAYBOUND)。
请注意,2种数据类型之间存在2个非常重要的差异。松散类型的声明Dim x(1 To 3, 1 To 3) As Integer
允许运行时确定数据区域中包含的内容。对于Dim x As Variant
赋值,您将获得Range.Value
数组的VARTYPE,这是兼容类型(这也是Variant
将编译的原因)。声明Dim x() As Variant
在编译时 已修复 。更重要的是,由于内存中SAFEARRAY结构的大小由维度数决定,因此编译器可以在编译时分配内存。但是,为COM调用返回的任意SAFEARRAY结构分配的内存量不能。此外,指向的存储区的大小由所包含类型的字节长度和元素总数确定。编译器通过禁止分配来防止不匹配的可能性。
实际上,这可能是您无法直接获取指向SAFEARRAY的指针的原因(唯一的方法是转换为Dim y(1 To 3, 1 To 3) As Integer
并手动取消引用指针#&1&# 39; s数据区域):
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
数组,其中每个元素长度为12个字节。您的第二个声明是Variant
数组,其中每个元素的长度为2个字节。在编译时,返回的内存区域的长度和适当的强制转换都不能可靠地确定。 VBA正在保护您免受访问冲突和/或运行时错误转换。
答案 1 :(得分:1)
如果我已正确理解您的查询,那么原因很简单。
要将数据从工作表传输到数组,需要调整大小。在您的示例中,它已修复。
试试这个
Dim x() As Variant
ReDim x(1 To 3, 1 To 3)
x = Range("A1:C3").Value
现在为什么是Variant
而不是String
?因为您不知道单元格内容的数据类型是什么。