tl; dr - Недостаточно информации, предоставленной определением типа возврата для компилятора, чтобы определить, как распределить память или как определить смещения для кастинга во время компиляции.
Чтобы понять, почему эти назначения несовместимы, это помогает понять основные структуры данных, которые VBA использует для представления каждого из них.
.Value
Недвижимость Range
возвращает Variant
. Тип, хранящийся в Variant
, определяется количеством ячеек в, которое Range
. Если имеется более одной ячейки, она возвращает Variant
, содержащий массив Variant
. Обратите внимание, что это имеет значение return a Variant
- в противном случае вам нужно будет индексировать массив в любое время, когда вы хотите получить значение из одной ячейки.
VBA является COM на основе языка, поэтому, когда вы заявляете что-то как Variant
, она хранится в COM VARIANT структуру, которая состоит из VARTYPE теге описывает содержащиеся на нем данные и либо указатель на исходных данных (типы предшествуют звездочку в союзе) или сами данные (типы, которым не предшествует звездочка в объединении). В памяти, это выглядит следующим образом:
Таким образом, когда вы используете назначение x = Range("A1:C3").Value
, вы получите VARTYPE, который описывает массив Variant
. Это важно, как вы увидите ниже.
Range
Если имеет только одну ячейку вы также получаете Variant
, но она содержит базовый тип - не массива.
Когда вы объявляете массив в VBA, он сохраняется в структуре COM SAFEARRAY, которая описывает массив таким образом, который делает его пригодным для использования другими COM-клиентами. В памяти он выглядит следующим образом (обратите внимание, что это один одномерный массив - SAFEARRAYBOUND в конце фактически массив с количеством элементов в CDIM):
Это в основном то, что вы получаете с декларация Dim x(1 To 3, 1 To 3) As Integer
(за исключением того, что в конце будет 2 SAFEARRAYBOUND).
Обратите внимание, что существует 2 очень важных различия между двумя типами данных. Объявление с произвольным типом Dim x As Variant
позволяет среде выполнения определять, что содержится в области данных. В случае назначения Range.Value
вы получите массив VARTYPE из Variant
, который является совместимым типом (именно поэтому Dim x() As Variant
скомпилирует). Декларация Dim y(1 To 3, 1 To 3) As Integer
- , установленная во время компиляции. Что еще более важно, поскольку размер структуры SAFEARRAY в памяти определяется количеством измерений, память может быть выделена компилятором во время компиляции. Однако объем памяти, выделенной для произвольной структуры SAFEARRAY, возвращаемой COM-вызовом, не может. Кроме того, размер размера указанной области памяти определяется длиной байта содержащегося типа и общим количеством элементов. Компилятор защищает от возможности несоответствия путем отказа от назначения.
На самом деле, это, вероятно, причина, почему вы не можете непосредственно получить указатель на 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 защищает вас от нарушений доступа и/или неудачных попыток выполнения.
Зачем вам *** ожидать, что *** сможет назначить «Variant», содержащий 2d-массив, который можно назначить 1-му массиву «Integer»? – Comintern
My bad, x должен был быть объявлен как двухмерный массив, но даже после этого я получаю ту же ошибку. –
Почему вы ожидаете, что сможете назначить «Variant», содержащий 2d-массив, который можно назначить ** * 2d массив *** 'Integer'? – Comintern