2016-07-31 13 views
2

habe ich eine Verwirrung über folgenden AussagenZuordnung Mehrzellenbereich Werte einer Variablen

Dim x as variant 
x=Range("A1:C3").value 

Nach oben Anweisung können wir x als eine zweidimensionale Matrix verwenden, aber wenn wir erklären, x als Array wie unten

Dim x(1 to 3,1 to 3) as integer 
x=Range("A1:C3").value 

Dann gibt mir die Verwendung des oben genannten Statemnet Fehler Kompilierzeit unter Angabe Can't assign to an array.

Mein Zweifel ist, wie der Code gut läuft, wenn x als Variante deklariert ist, aber mir Fehler gibt, wenn es als Array deklariert wird.

+0

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

+0

Mein schlechtes, x hätte als zweidimensionales Array deklariert werden sollen, aber selbst danach bekomme ich den gleichen Fehler –

+0

Warum sollten Sie eine 'Variant' mit einem 2D-Array zuweisen können, das einem ** zugewiesen werden kann? * 2d Array *** von 'Integer'? – Comintern

Antwort

1

Wenn ich Ihre Anfrage richtig verstanden habe, dann ist der Grund sehr einfach.

Um die Daten von einem Arbeitsblatt in ein Array zu übertragen, muss die Größe geändert werden können. In Ihrem Beispiel ist es behoben.

dieses

Dim x() As Variant 

ReDim x(1 To 3, 1 To 3) 

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

jetzt versuchen, warum ein Variant und kein String? Weil Sie nicht wissen, was der Datentyp des Zelleninhalts ist.

3

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:

Variant in memory

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):

SafeArray in memory

, 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.

Verwandte Themen