tl; dr - Es gibt nicht genügend Informationen, die von der Rückgabetypdefinition für den Compiler bereitgestellt werden, um zu bestimmen, wie Speicher zugewiesen wird oder Offsets für das Umwandeln zur Kompilierungszeit festgelegt werden.
Um zu verstehen, warum diese Zuweisungen nicht kompatibel sind, hilft es, die zugrunde liegenden Datenstrukturen zu verstehen, die VBA verwendet, um sie zu repräsentieren.
Die .Value
Eigenschaft einer Range
gibt eine Variant
zurück. Der in der Variant
gespeicherte Typ wird durch die Anzahl der Zellen in, die Range
ist, bestimmt. Wenn es mehr als eine Zelle gibt, gibt es eine Variant
zurück, die ein Array von Variant
enthält. Beachten Sie, dass hat geben Sie eine Variant
zurück - sonst müssten Sie in ein Array zu jeder Zeit, die Sie den Wert aus einer einzelnen Zelle wollte indexieren.
VBA ist eine COM-basierte Sprache, so, wenn man etwas als Variant
erklärt, wird es in einer COM VARIANT Struktur gespeichert, die ein VARTYPE bestehen aus dem die darin enthaltenen Daten und entweder einen Zeiger auf die zugrunde liegenden Daten (Typen mit voran beschreibt ein Stern in der Union) oder die Daten selbst (Typen, denen in der Union kein Stern vorangestellt ist). Im Gedächtnis, sieht es wie folgt aus:
Also, wenn Sie die Zuordnung verwenden x = Range("A1:C3").Value
, erhalten Sie eine VARTYPE, die ein Array von Variant
beschreibt. Dies ist wichtig, wie Sie unten sehen werden.
Wenn die Range
hat nur eine Zelle, die Sie auch ein Variant
bekommen, aber es enthält den zugrunde liegenden Typ - nicht ein Array.
Wenn Sie ein Array in VBA deklarieren, wird es in einer COM SAFEARRAY Struktur gespeichert, die das Array so beschreibt, dass es von anderen COM-Clients verwendet werden kann. In Erinnerung sieht es wie folgt aus (beachten Sie, dass dies eine eindimensionale Matrix - die SAFEARRAYBOUND am Ende tatsächlich ein Array mit der Anzahl der Elemente in Cdim):
, die im Grunde ist das, was Sie bekommen mit die Deklaration Dim x(1 To 3, 1 To 3) As Integer
(mit Ausnahme von 2 SAFEARRAYBOUNDs am Ende).
Beachten Sie, dass zwischen den 2 Datentypen 2 sehr wichtige Unterschiede bestehen. Die lose typisierte Deklaration Dim x As Variant
ermöglicht der Laufzeit festzustellen, was im Datenbereich enthalten ist. Im Fall der Range.Value
Zuweisung erhalten Sie ein VARTYPE Array von Variant
, das ein kompatibler Typ ist (weshalb auch Dim x() As Variant
kompiliert wird). Die Deklaration Dim y(1 To 3, 1 To 3) As Integer
ist zur Kompilierzeit behoben. Was noch wichtiger ist, da die Größe einer SAFEARRAY-Struktur im Speicher durch die Anzahl der Dimensionen bestimmt wird, kann der Speicher vom Compiler zur Kompilierungszeit zugeordnet werden. Die Speichermenge, die für eine beliebige SAFEARRAY-Struktur reserviert wird, die von einem COM-Aufruf zurückgegeben wird, kann jedoch nicht. Ferner wird die Größe des Speicherbereichs, auf den gezeigt wird, durch die Bytelänge des enthaltenen Typs und die Gesamtzahl der Elemente bestimmt. Der Compiler schützt vor der Möglichkeit einer Nichtübereinstimmung, indem er die Zuweisung verweigert.
In der Tat, das ist wahrscheinlich der Grund, warum Sie nicht direkt einen Zeiger auf ein SAFEARRAY erhalten können (den einzigen Weg, dies zu tun ist, um ein Variant
und manuell die Zeiger dereferenzieren zu werfen daraus Datenbereich ist):
Also brechen Sie es auf, Sie können dies nicht tun, weil der Compiler nicht genügend Informationen hat, um die Laufzeit-Cast sicher zu machen. Wenn Sie ein wenig Stocher unter der Haube tun wollen, zeigt dieser Code, was unter der Haube passiert:
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
Ihre erste Erklärung ist ein Array von Variant
, wobei jedes Element 12 Bytes lang ist. Ihre zweite Deklaration ist ein Array von Integer
, wobei jedes Element 2 Bytes lang ist. Weder die Länge des zurückgegebenen Speicherbereichs noch die passende Besetzung kann zur Kompilierzeit zuverlässig ermittelt werden. VBA schützt Sie vor Zugriffsverletzungen und/oder Laufzeitfehlern.
Warum würden Sie *** erwarten, dass Sie in der Lage sind, eine 'Variante' zuzuweisen, die ein 2D-Array enthält, das einem 1D-Array von 'Integer' zugeordnet werden kann? – Comintern
Mein schlechtes, x hätte als zweidimensionales Array deklariert werden sollen, aber selbst danach bekomme ich den gleichen Fehler –
Warum sollten Sie eine 'Variant' mit einem 2D-Array zuweisen können, das einem ** zugewiesen werden kann? * 2d Array *** von 'Integer'? – Comintern