Was bedeutet eigentlich persistent? 'Persistente Klassen' sind Klassenmodule, die Instanzen erstellen, deren enthaltene Daten auch nach dem Zerstören einer Instanz erhalten bleiben. Bei manchen Klassen ist es sinnvoll, die aktuelle Einstellung der Eigenschaften, die enthaltenen Daten, benötigte Tabellen (Hilfswerte) oder 'innere Zustände' (z.B. Ergebnisse von Berechnungen oder vorgenommene Sortierungen) einer Klassen-Instanz für die spätere Wiederverwendung zu speichern. Schlüssel in der Windows-Registry oder die Init-, Read- Write-Properties-Prozeduren sog. 'persistenter' Klassen in ActiveX-Komponenten sind nur für die Speicherung einer geringen Anzahl von Informationen geeignet. Große Datenmengen müssen in einer Datei abgelegt werden. Es ist in solchen Fällen zweckmäßig, alle Variablen, die die zu speichernden Daten enthalten, in einem benutzerdefinierten Datentyp (UDT) zusammenzufassen. UDT-Variable können durch einen einzigen PUT#-Befehl in eine Datei geschrieben und per GET# auch wieder gelesen werden. Der Zugriff auf die Eigenschaften und sonstigen Hilfsvariablen der Klasse erfolgt dann über die Elemente einer UDT-Variable, die im Deklarationsteil der Klasse definiert wird. Die Leistungsfähigkeit des binären Dateizugriffs durch PUT/GET wird oft unterschätzt.
Das Besondere dabei ist die hohe Geschwindigkeit dieser Lese- und Schreibvorgänge. Große Datenmengen können in Sekundenbruchteilen gelesen und geschrieben werden. Der Ablauf vollzieht sich um ein vielfaches schneller als bei umfangreicheren Registry-Zugriffen, sequentiellen Datei-Streams oder Datenbank-Transaktionen. Aus diesem Grund ist es möglich, im TERMINATE-Ereignis einer Klasse eine Datei mit Daten zu füllen (PUT) und im INITIALIZE-Ereignis der Klasse diese Datei wieder zu lesen (GET). Das Programm wird dadurch unter normalen Umständen nicht 'ausgebremst'. Es bietet sich somit eine komfortable Möglichkeit auch umfangreichere Datenmengen, die in einer Klasseninstanz enthalten sind, 'automatisch' zu speichern und zu laden. Einige Einschränkungen sind zu beachten:
Die Funktion GET erwartet beim binären Dateizugriff, dass die Daten in der Datei der UDT-Variable entsprechen, die eingelesen werden soll. Gibt es dabei Diskrepanzen, können Programmabstürze oder andere unvorhergesehene Ereignisse eintreten. Zumindest stimmt dann die Belegung der Elemente der UDT-Variable nicht. Es muss deshalb sicher gestellt werden, dass der Inhalt der Datei mit der UDT-Variable korrespondiert, in die die Daten gelesen werden sollen. Anwendungsbeispiel: Die aktuellen Daten der Klasse werden (falls das Array dimensioniert ist) im Terminate-Ereignis durch PUT gespeichert und im INITIALIZE-Ereignis durch GET gelesen. Die Funktion 'Variant_Array_Demo' demonstriert die Arbeitsweise der Klasse 'clsVariantArray'. Beim Ausführen erzeugt das Beispiel zwei Dateien im Anwendungspfad ('clsVariantArray.mbv', 'Inhalt_Von_VariantArray.mbv'). Bereits vorhandene gleichnamige Dateien werden ohne Nachfrage überschrieben! Man beachte, dass die Funktionen PUT/GET das dynamisch deklarierte Variant-Array, dessen Feld-Elemente verschiedene Untertypen enthalten, auch als UDT-Komponente verarbeiten können. Hinweise: Falls in einer Klassendatei die klassenglobalen PRIVATE-Konstanten 'cVersion', 'cMemberFile' und das Ereignis 'FehlZugriff' ergänzt worden sind und die zu speichernden Daten als Elemente in der klassenglobalen PRIVATE-UDT-Variable 'gMembers' enthalten sind, können die Routinen 'SaveMembers' und 'LoadMembers' als Klassen-Methoden aus dem Anwendungsbeispiel übernommen werden. Sub VariantArray_Demo() ' Anwendungsbeispiel für Klasse 'clsVariantArray' ' Zugriffsvariable für Instanzen der Klasse ' (lokale Deklaration: das Ereignis 'Fehlzugriff' ' wird nicht ausgelöst) Dim a As clsVariantArray Dim b As clsVariantArray Dim c As clsVariantArray Dim arr() As Variant ' Array für Vergleich Dim i As Long, N As Long ' Loop Dim wert As Variant ' Zuweisungsvariable Dim File As String ' Datenausgabe 'c' ' separates File für Ausgabe des Inhalts einer Instanz ' (wird überschrieben!) File = App.Path + "\Inhalt_Von_VariantArray.mbv" ' eine Instanz der Klasse 'clsVariantArray' anfordern Set a = New clsVariantArray ' ggf. bereits bestehende Klassenbez. Datendatei löschen a.ClearFile N = 10000 ' Arraygröße festlegen ReDim arr(1 To N) ' Array für Vergleich dimensionieren a.Dimensionieren 1, N ' Klassen-Array dimensionieren (1->N) ' Einen Kommentar eintragen (nach dem Dimensionieren!) a.Kommentar = "Array für Testzwecke - erstellt: " + CStr(Now) ' Array und Vergleichsarray mit Zufallszahlen füllen For i = 1 To N ' Double-Zufallszahl (-10000 bis 10000) erstellen wert = CDbl(Rnd * 1000) If Rnd > 0.5 Then wert = -wert ' Verschiedene Variant-Untertypen erstellen If i Mod 5 = 1 Then wert = Int(wert) ' Integer ElseIf i Mod 5 = 2 Then wert = CStr(wert) ' String ElseIf i Mod 5 = 3 Then wert = CCur(wert) ' Currency ElseIf i Mod 5 = 4 Then wert = CDec(i) ' Dezimal End If arr(i) = wert ' Vergleichsarray füllen a.Zugriff(i) = arr(i) ' Klassenarray füllen ' Inhalt von 'a' zwischendurch ' in einem separaten File speichern If i = N \ 2 Then a.SaveMembers File End If Next i ' 100 weitere Zuweisungen, die nicht in das ' Vergleichsarray eingetragen werden For i = 1 To 500 Step 5 a.Zugriff(i) = CDbl(Rnd) Next i ' Instanz der Klasse zerstören ' Die Membervariablen werden gespeichert (TERMINATE) Set a = Nothing ' Neue Instanzen der Klasse anfordern: ' Die gespeicherten Membervariablen werden in alle ' Instanzen der Klasse geladen (INITIALIZE) Set a = New clsVariantArray Set b = New clsVariantArray Set c = New clsVariantArray ' Die 100 weiteren Zuweisungen wieder rückgängig machen: ' in den Objekt-Instanzen 'a' und 'b'! While a.Rückgängig_Machen_Möglich a.Rückgängig_Machen b.Rückgängig_Machen Wend ' Die Instanz 'c' soll nicht den Inhalt der ' Standard-Klassendatei enthalten, sondern ' das zwischengespeicherte Array in 'File' c.LoadMembers File ' c' enthält jetzt nur die Werte 1 bis N \ 2 For i = c.Untergrenze To N If i <= N \ 2 Then If c.Zugriff(i) <> a.Zugriff(i) Then Stop Else ' leerer Bereich von 'c' If c.Zugriff(i) <> vbEmpty Then Stop End If Next i ' Prüfen des Inhalts der Klassen-Instanzen: ' Es muss wieder der Inhalt des Vergleichsarray ' hergestellt sein (sonst STOP) For i = 1 To N ' Identiät der Variant-Untertypen prüfen If VarType(arr(i)) <> VarType(a.Zugriff(i)) Then Stop ' Identität der Variant-Werte prüfen If arr(i) <> a.Zugriff(i) Then Stop If arr(i) <> b.Zugriff(i) Then Stop Next i a.Clear ' Inhalt von Array 'a' Löschen ' Inhalt des Array in 'b' auf 'a' zuweisen (kopieren) a.ArrayZuweisen = b ' In 'a' muss jetzt wieder 'b' stehen For i = b.Untergrenze To b.Obergrenze If a.Zugriff(i) <> b.Zugriff(i) Then Stop Next i ' Der Inghalt der nur teilweise gefüllten ' Instanz 'c' soll nicht in der klassenbez. ' Daten-Datei gespeichert werden: c.Speichern_Verboten = True MsgBox "Variant-Array Demo abgeschlossen" ' Hier werden die lokal deklarierten Instanzen ' der Klasse automatisch zerstört, ' aber die Daten-Inhalte zuvor gespeichert (nur a,b) ' Wer auf 'END SUB' einen Breakpoint setzt, ' kann den Ablauf der Terminate-Ereignisse verfolgen End Sub |