Ich weiß, dass Sie nichts von einer Aufzeichnung ableiten können, aber ich bin mir nicht sicher, wie ich mein Problem in einem Satz zusammenfassen soll. Bearbeiten Sie den Titel, wenn Sie dies tun.
Was ich hier machen möchte, ist, ein Array von einem generischen Typ zu machen, der eine von X Arten von Typen sein kann, das Array würde mit diesen benutzerdefinierten Typen gefüllt werden (sie haben unterschiedliche Felder und das ist was wichtig ist). Der einfache Weg besteht darin, nur ein Array von Variantendatensätzen zu erstellen, wobei jede Variante ihren eigenen Typ hat, aber Identifizierer wie diese natürlich nicht neu deklarieren kann:
%Vor% Durch das Ändern von SubRec
in SubRec1, SubRec2... SubRecN
wird das Referenzieren zwar schmerzhaft, aber nicht unmöglich.
Und seit ich nach alternativen Lösungen für das oben genannte Problem suchte, kamen mir Klassen in den Sinn.
Das offensichtlichste Beispiel, um zu demonstrieren, was ich zu erreichen versuche, ist TObject
, ein Array von diesen kann vielen verschiedenen Dingen zugewiesen werden. Das ist was ich will, aber mit Aufzeichnungen (und das ist unmöglich zu machen), weil ich in der Lage sein möchte, die Datensätze in Datei zu speichern und sie auch wieder zu lesen (auch weil ich damit schon vertraut bin). Es ist kein Problem, meine eigene einfache Klasse zu erstellen, indem ich eine Nachkommenklasse daraus mache, um meinen Subtyp zu repräsentieren - das kann ich. Aber wie wäre es damit, das zu schreiben und es zurückzulesen? Das läuft auf die Serialisierung hinaus, von der ich keine Ahnung habe, wie es zu machen ist. Nach meinen Erfahrungen ist das nicht so einfach und die Klasse muss von TComponent
abstammen.
Macht es irgendeinen Unterschied, wenn ich die Klasse wie oben mache? Es ist nichts Besonderes und hat höchstens 10 Felder, darunter ein paar benutzerdefinierte Typen.
Wenn man die Serialisierung beiseite legt (nur weil ich viel über dieses Thema zu lesen habe), könnte die Verwendung von Klassen hier ebenfalls nicht in Frage kommen.
Was sind meine Optionen an diesem Punkt? Soll ich Aufzeichnungen verwerfen und das mit Klassen versuchen? Oder wäre es viel unkomplizierter, nur an Aufzeichnungen festzuhalten und sich mit der Variante "Begrenzung" auseinander zu setzen? Es geht mir nur darum, zu lernen, und wenn mich der Klassenansatz explodieren könnte, dann würde ich es schlauer machen. Ich habe auch nur in TList
nachgeschaut (habe es nie benutzt), aber es scheint, dass es nicht so gut mit den Rekorden zusammenpasst, vielleicht kann es auch gemacht werden, aber das könnte momentan aus meiner Liga sein. Ich bin offen für jede Art von Vorschlägen. Was mache ich?
Die "natürliche" Art, solche Daten zu verarbeiten, ist die Verwendung von class
und nicht von record
. Es wird viel einfacher sein, sowohl zur Definitionszeit als auch bei der Implementierung zu arbeiten. Insbesondere virtual
-Methoden sind sehr leistungsfähig, um einen Prozess für eine bestimmte Art von Klasse anzupassen. Verwenden Sie dann in neueren Versionen von Delphi ein TList/TObjectList
oder ein TCollection
oder ein generisches Array, um die Liste zu speichern.
Über die Serialisierung gibt es mehrere Möglichkeiten. Siehe Delphi: Speichern Sie Daten in einer Struktur
In Ihrem speziellen Fall kommt die Schwierigkeit von der Art der "Variante", die Sie verwenden. IMHO der Hauptnachteil ist, dass der Compiler ablehnen wird, irgendeine Referenz-gezählte Art der Variable (z. B. ein string
) innerhalb des "Variante" Teils zu setzen. Sie können also nur "einfache" Variablen (wie integer
) innerhalb dieses "Variant" -Teils schreiben. Eine große Einschränkung IMHO, die das Interesse dieser Lösung reduziert.
Eine andere Möglichkeit könnte darin bestehen, die Art von Aufzeichnung zu Beginn ihrer Definition zu speichern, z. mit einem RecType: integer
oder noch besser mit einem RecType: TEnumerationType
, das expliziter ist als eine Zahl. Aber Sie müssen eine Menge Code von Hand schreiben, und arbeitet mit Zeigern, die ein wenig fehleranfällig ist, wenn Sie mit der Zeigercodierung nicht sehr flüssig sind.
Sie können also auch die Typinformationen des Datensatzes speichern, auf die über TypeInfo(aRecordVariable)
zugegriffen werden kann. Dann können Sie FillChar
verwenden, um den Inhalt des Datensatzes direkt nach der Zuweisung auf Null zu initialisieren, dann verwenden Sie die folgende Funktion, um den Inhalt des Datensatzes zu finalisieren, direkt nach der Zuweisung (das ist Dispose()
intern) und Sie sollten es andernfalls aufrufen Sie werden Speicher verlieren):
Aber solch ein Implementierungsmuster wird das Rad einfach neu erfinden! Tatsächlich ist class
implementiert: Das erste Element einer TObject
-Instanz ist ein Zeiger auf seine ClassType
:
Es gibt auch eine andere Struktur in Delphi, die object
heißt. Es ist eine Art record
, aber es unterstützt Vererbung - siehe dieser Artikel . Es ist der alte Stil der OOP-Programmierung in Turbo Pascal 5.5 Tage, veraltet, aber immer noch verfügbar. Beachten Sie, dass ich in neueren Versionen von. Ein seltsames Kompilierungsproblem entdeckt habe Delphi : Manchmal wird ein object
auf dem Stack nicht immer initialisiert.
Sehen Sie sich unseren TDynArray
-Wrapper und die damit verbundenen Funktionen an, der in der Lage ist, jeden record
-Inhalt in Binärcode oder JSON zu serialisieren. Siehe Fragen zu Delphi (win32) Serialisierungsbibliotheken . Es wird mit varianten Datensätzen arbeiten, auch wenn sie ein string
in ihrem unvarianten Teil enthalten, während ein einfaches "Write / BlockWrite" nicht mit Referenzfeldern funktionieren wird.
Sie fassen die Serialisierung zusammen, indem Sie "alles mit einem einzigen BlockWrite
-Aufruf auf die Festplatte schreiben". Sie können beliebige Objekte serialisieren, unabhängig davon, ob sie von TComponent
oder TPersistent
abstammen.
Obwohl das Schreiben von allem mit einem einzigen BlockWrite
-Aufruf zunächst praktisch ist, werden Sie schnell feststellen, dass es nicht wirklich das ist, was Sie wollen, wenn Ihre gewünschten Aufnahmetypen etwas besonders Interessantes speichern (Strings, dynamische Arrays, Interfaces, Objekte oder andere Referenz- oder Zeiger-basierte Typen).
Sie werden wahrscheinlich auch abweichende Datensätze finden, da Sie auf den kleinsten gemeinsamen Nenner codieren werden. Sie können auf nichts im Datensatz zugreifen, ohne den tatsächlich enthaltenen Typ zu überprüfen, und die Größe selbst der kleinsten Datenmenge belegt den gleichen Speicherplatz wie der größte Datentyp.
Die Frage scheint Polymorphismus zu beschreiben, also können Sie auch das, was die Sprache dafür bereits bietet, umarmen. Verwenden Sie ein Array (oder eine Liste oder einen anderen Container) von Objekten. Dann können Sie virtuelle Methoden verwenden, um alle einheitlich zu behandeln. Sie können den dynamischen Versand für Datensätze implementieren, wenn Sie möchten (z. B. geben Sie jedem Datensatz einen Funktionszeiger, der auf eine Funktion verweist, die mit dem enthaltenen Datentyp dieses Datensatzes umgehen kann), aber am Ende werden Sie wahrscheinlich feststellen, dass Sie Klassen neu erfinden .
Um dies mit Datensätzen zu tun, würden Sie verschiedene Datensatztypen erstellen, die ein oder mehrere gemeinsame Felder haben, und diese Felder dann in den generischen Datensatz einfügen. Dann können Sie bei Bedarf einfach einen Zeiger auf einen generischen Datensatz in einen Zeiger auf einen bestimmten Datensatz schreiben. Zum Beispiel:
%Vor% Wie für die Serialisierung benötigen Sie TComponent
nicht. Sie können Datensätze serialisieren, Sie müssen es nur manuell tun. Schreiben Sie zum Schreiben zunächst den Wert RecType
und schreiben Sie dann die datensatzspezifischen Werte als nächstes aus. Lesen Sie zum Lesen zuerst den RecType
-Wert, erstellen Sie dann den entsprechenden Datensatztyp für diesen Wert, und lesen Sie dann die datensatzspezifischen Werte darin.:
.
%Vor%.
%Vor%.
%Vor%.
%Vor%