Kontakt
DSVGO
Historie | |
---|---|
27.02.2008 | Workaround für Microsoft-Bug in Collection (arbeitet Fehlerhaft, wenn Key ein vbNullChar enthält); Vielen Dank an Franz Reiter aus Österreich für den Hinweis! |
12.04.2002 | Hinweis auf GlobalMultiUse-Klasse für Konstruktor |
17.04.2001 | Erste Version |
Im Rahmen dieses Artikels wird eine Klasse vorgestellt, die Array-ähnlichen Zugriff auf (theoretisch) beliebig große Datenmengen ermöglicht. Nach Festlegung des Ist- und Soll-Zustands (s.a. Beispiel) wird der realisierte Quelltext genaustens analysiert. Ein objektorientiertes Schmankerl ist die Realisierung eines Konstruktors für die vorgestellte Klasse.
Ein Feld in VB ist im Vergleich zu denen der meisten anderen Programmiersprachen äußerst flexibel. So ermöglicht ReDim Preserve dynamische Größenänderungen, ohne das die Inhalte verloren gehen. Einschränkungen gibt es aber in den folgenden Bereichen:
Nachfolgend wird die BigArray-Klasse vorgestellt, welche die o.g. Einschränkungen nicht hat. Daher ist sie für die Darstellung von "dünn-besetzten Matrixen" besonders geeignet.
Die Klasse bietet folgende Methoden und Eigenschaften:
obj.Clear [Default]
löscht den gesamten Inhalt; optional kann ein Default-Wert angegeben werden. (Default-Werte werden nicht wirklich gespeichert!)
x = obj.Default
liefert den aktuellen Default-Wert zurück.
x = obj.Exists(Index1, Index2, ...)
zeigt an, ob ein Wert existiert.
x = obj.Item(Index1, Index2, ...)
gibt den Wert zurück; existiert der Wert nicht, so wird der Default-Wert zurückgegeben.
obj.Item(Index1, Index2, ...) = x
speichert den angegebenen Wert.
Set obj.Item(Index1, Index2, ...) = x
speichert das angegebene Objekt (genauer: den Objekt-Verweis).
Man beachte, dass alle Index-Angaben sowohl numerisch als auch alphanumerisch (deswegen "assoziativ") sein können. Item ist die Default-Eigenschaft. Daher kann verkürzend auch folgendes geschrieben werden:
x = obj(Index1, Index2, ...)
obj(Index1, Index2, ...) = x
Set obj(Index1, Index2, ...) = x
Der Zugriff kann also exakt wie bei einem Standard-Array erfolgen.
Tipp: Für Freunde der Objektorientierung wird unten auch gezeigt, wie man sogenannte Konstruktoren in VB elegant simulieren kann, so dass der Klassen-Anwender dies gar nicht bemerkt. Eine neues BigArray kann dadurch in einem Rutsch initialisiert werden:
Dim obj As BigArray
Set obj = BigArray([Default])
Im folgenden Beispiel wird ein BigArray ba mit Default-Wert "nix drin" angelegt. Es wird ein Element in eine bestimmte Position geschrieben, danach wird es wieder überschrieben. Anschließend wird das globale Screen-Objekt auch noch abgelegt, und dann werden alle Elemente aufgelistet. Nach dem Zurücksetzen des Arrays wird eine bestimmte Position abgefragt, und dann getestet, ob dort überhaupt ein Wert gespeichert ist.
Dim ba As BigArray Dim v As Variant 'BigArray anlegen: Set ba = BigArray("nix drin") 'Strings rein-/überschreiben: ba(2, 3, 4, "Wacker", 5) = "Willi" ba(2, 3, 4, "Wacker", 5) = "Wusel" 'Objekt reinschreiben: Set ba(2) = Screen 'Alle Elemente auflisten: For Each v In ba Print TypeName(v) 'ergibt "String" und "Screen" Next v 'Array löschen: ba.Clear "ganz leer" Print ba(2) 'ergibt "ganz leer" Print ba.Exists(2) 'ergibt "False"
Legen Sie ein neues Klassenmodul mit dem Namen "BigArray" an. Fügen Sie im Deklarationsteil den Klassen-/Objekt-weit reichenden Code ein:
'Status-Variablen: Private pDefault As Variant 'Default-Wert Private pItems As Collection 'Daten-Speicher 'Ein neues BigArray ist "jungfräulich": Private Sub Class_Initialize() Clear End Sub
Folgende Routinen erleichtern die Realisierung der Methoden und Eigenschaften. Basis ist eine Collection, in der ein Element unter einem bestimmten Key-String (gebildet aus der Positions-Angabe) abgelegt wird, falls das Element nicht dem Default-Wert entspricht.
'Index-Liste in Key-String umwandeln: Private Function IndexKey(ByRef Index As Variant) As String Dim i As Long For i = LBound(Index) To UBound(Index) IndexKey = Index(i) & vbTab & IndexKey 'vbNullChar' funzt nicht!?! Next i End Function 'Entspricht Element dem Default-Wert? Private Function IsDefault(ByRef Value As Variant) As Boolean If IsNull(pDefault) Then IsDefault = IsNull(Value) ElseIf IsEmpty(pDefault) Then IsDefault = IsEmpty(Value) ElseIf IsNull(Value) Or IsEmpty(Value) Then Exit Function ElseIf IsObject(pDefault) Then If IsObject(Value) Then IsDefault = (Value Is pDefault) ElseIf Not IsObject(Value) Then IsDefault = (Value = pDefault) End If End Function 'Element unter Key speichern: Private Sub ItemAdd(ByRef Key As String, ByRef Value As Variant) KeyRemove Key If Not IsDefault(Value) Then pItems.Add Value, Key End Sub 'Existiert Element unter Key? Private Function KeyExists(ByRef Key As String) As Boolean On Error Resume Next KeyExists = Len(TypeName(pItems.Item(Key))) On Error GoTo 0 End Function 'Element unter Key löschen: Private Sub KeyRemove(ByRef Key As String) On Error Resume Next pItems.Remove Key On Error GoTo 0 End Sub
Die nach außen sichtbaren Routinen (sprich die öffentlichen Methoden und Eigenschaften) brauchen nur noch die oben gezeigten Hilfs-Routinen zur richtigen Zeit aufrufen. Dabei muss u.U. unterschieden werden, ob es sich bei dem Element um einen einfachen Wert oder um ein Objekt handelt.
'BigArray zurücksetzen: Public Sub Clear(Optional aDefault As Variant) Set pItems = New Collection If Not IsMissing(aDefault) Then If IsObject(aDefault) Then Set pDefault = aDefault Else pDefault = aDefault End If End If End Sub 'Default-Wert auslesen: Public Property Get Default() As Variant Default = pDefault End Property 'Existiert Element an Index-Position? Public Property Get Exists(ParamArray Index()) As Boolean Dim Key As String Key = IndexKey(CVar(Index)) Exists = KeyExists(Key) End Property 'Element an Index-Position auslesen: 'Procedure ID: (Default) Public Property Get Item(ParamArray Index()) As Variant Dim Key As String Key = IndexKey(CVar(Index)) If KeyExists(Key) Then 'Wert auslesen: If IsObject(pItems.Item(Key)) Then Set Item = pItems.Item(Key) Else Item = pItems.Item(Key) End If Else 'Default zurückgeben: If IsObject(pDefault) Then Set Item = pDefault Else Item = pDefault End If End If End Property 'Element (einfacher Wert) speichern: Public Property Let Item(ParamArray Index(), aItem As Variant) ItemAdd IndexKey(CVar(Index)), aItem End Property 'Element (Objekt) speichern: Public Property Set Item(ParamArray Index(), aItem As Variant) ItemAdd IndexKey(CVar(Index)), aItem End Property 'For...Each für BigArray einrichten: 'Procedure ID: -4 Public Function NewEnum() As IUnknown Set NewEnum = pItems.[_NewEnum] End Function
Vergessen Sie nicht, unter "Tools / Procedure Attributes" die angegebenen "Procedure IDs" einzustellen. Ansonsten funktioniert weder der komfortable Default-Zugriff auf die Item-Eigenschaft, noch die Enumeration durch alle im BigArray gespeicherten Elemente.
Ein Konstruktor erlaubt die Generierung eines Objekts, indem man den Namen der Klasse wie eine Funktion aufruft. Dabei können alle eventuell für die Initialisierung notwendigen Informationen gleich als Parameter mit übergeben werden.
Legen Sie ein neues Standard-Modul mit dem Namen "BigArrayConst" an. Fügen Sie einfach folgenden Code ein:
Public Function BigArray( _ Optional ByRef Default As Variant = Empty _ ) As BigArray Set BigArray = New BigArray 'Neues Objekt generieren BigArray.Clear Default 'Objekt initialisieren End Function
Wenn Sie zukünftig beide BigArray-Dateien in ihre Projekte einbinden, genügt der im Beispiel gezeigte Aufruf zur vollständigen Einrichtung eines BigArray-Objekts.
Möchten Sie aus dem Ganzen eine selbstständige ActiveX-Komponente erstellen, so nennen Sie die ActiveX-DLL etwa "BigArrayLib", und erstellen den Konstruktor (statt in einem Standard-Modul) in einer GlobalMultiUse-Klasse, damit er auch von überall her aufrufbar ist.
© Jost Schwider, 17.04.2001-27.02.2008 - http://vb-tec.de/bigarray.htm