![]() |
TCollection und TCollectionItem
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
ich gebe an unserem örtlichen Gymnasium eine Informatik AG. Dort habe ich gerade den Schülern gezeigt, wie man mit Hilfe von TCollection und TCollectionItem sehr einfach Listen (auch n-dimensionale Listen) speichern kann. Ich denke, der Code ist auch für die Code-Library interessant. Das Verfahren nutzt das Delphi-Streamingsystem. Delphi selbst nutzt dieses Technik um z.B. die Komponenteneigenschaften, die im OI editiert werden, in die dfm-Datei zu speichern. Bei TStatusbar ist die Panels-Eigenschaft eine Nachfahre von TCollection. Ein einzelnes Panel dieser Collection ist vom Type TCollectionItem. Alle published Eigenschaften eines Panels werden beim speichern vom Streamingsystem erfasst. Ganz automatisch - ohne unser zu tun. Interessant ist, wenn eine published Eigenschaft von TCollectionItem eine TCollection ist wird auch diese automatisch gespeichert. Damit hätten wir praktisch schon ein 2-dimensionales "Array" gespeichert. Das kann man beliebig fortsetzten. Der Anhang enthält eine Powerpointdatei. Darin habe ich versucht es graphisch darzustellen. Aber jetzt zum Beispielprogramm: Das Programm hat ein TEdit, 2 TListBoxen und 2 TButtons. Über das TEdit kann man Einträge hinzufügen (auf Enter drücken). Damit werden automatisch zu jedem Eintrag 5 Zahlen hinzugefügt (2. TListBox). Damit haben wir eine 2-dimensionale Struktur. Zu jedem Eintrag (TAddressItem) gehören 5 Zahlen (TNumber). Die können jetzt gespeichert und geladen werden. TAddressItem entspricht einem Eintrag. Jedes TAddressItem hat in seiner published Abschnitt eine Eigenschaft vom Type TNumbers. TNumbers ist ein Nachfahre von TCollection und verwaltet die einzelnen TNumber (also die o.g. 5 Zahlen zu jedem Eintrag). Jetzt kommt ein äußerst interessanter Punkt: Wenn die published Eigenschaften von einem TCollectionItem (hier TAddressItem und TNumber) erweitert werden, können die alten Datein immer noch gelesen werden. Die neuen Eigenschaft dann mit Null initialisiert !!!!!!!!!!!!! Das entspricht einer Änderung des Dateiformats. Das ist mit typisierten Dateien nicht möglich. Die unit collection.pas enthält den interessanten Code. |
Re: TCollection und TCollectionItem
:-D Moin.
Ja mit collection und dem streaming system kann man lustige sachen machen. Hab mich auch mal drann vergriffen, hab aber das 'owner' objekt nur in den stream gefaked und nicht extra als dummi erzeugt und dann direkt mit WriteCollection gearbeitet. Vielleicht interssiert es dich ja: ![]() Was ich auch einen sehr interessanten aspekt des streaming systems finde, ist die möglichkeit dynamische binäre properties zu definieren :wink: PS: Insbesondere wäre für dich vielleicht die möglichkeit von nutzen, den binären DFM-strom in das text-DFM format zu konvertieren, das macht die ganze sache schön lesbar un editierbar...kennt man ja. |
Re: TCollection und TCollectionItem
@maximov: GUter Tip - werde ich mir mal reinziehen.
|
Re: TCollection und TCollectionItem
@maximov: Sehr guter Vorschlag. Wenn man auf die Option verzichtet die Datei im Textformat zu speichern und die Code auf das notwendigste reduziert ist Dein Vorschlag besser als meiner.
Dadurch, dass ich den Umweg über ein TComponent gehe sind die Daten in den Items für eine Schrecksekunde doppelt im Speicher. Einmal in der Collection und über Assigen in der Items property des Dummies. Für das Textformat schreibst Du erst mal alles in einen TMemorystream. In dem Moment sind die Daten ebenfalls doppelt vorhanden. Wenn man jetzt aber auf TMemoryStream verzichtet und über TWriter/TReader direkt in den Stream schreibt sind die Daten nicht doppelt vorhanden. Wie gesagt, wenn auf das Textformat verzichtet werden kann finde ich diese Lösung besser. Vielen Dank für die Anregung !!!
Delphi-Quellcode:
unit CollectionExt;
interface Uses SysUtils, Classes; Type TExtCollection = class(TCollection) private function GetFormatSignature: String; public procedure SaveToFile(const Filename : TFilename); procedure SaveToStream(Stream : TStream); procedure LoadFromFile(const Filename : TFilename); procedure LoadFromStream(Stream : TStream); end; implementation const iFilerBufferSize = 4096; { TExtCollection } function TExtCollection.GetFormatSignature: String; begin Result := ItemClass.ClassName; end; procedure TExtCollection.LoadFromFile(const Filename: TFilename); var FileStream : TFileStream; begin Clear; FileStream:=TFileStream.Create(Filename,fmOpenRead); Try LoadFromStream(FileStream); Finally FileStream.Free; end; end; procedure TExtCollection.LoadFromStream(Stream: TStream); var Reader : TReader; begin Reader:=TReader.Create(Stream,iFilerBufferSize); Try Reader.ReadValue; Reader.ReadCollection(Self); Finally Reader.Free; end; end; procedure TExtCollection.SaveToFile(const Filename: TFilename); var FileStream : TFileStream; begin FileStream:=TFileStream.Create(Filename,fmCreate); Try SaveToStream(FileStream); Finally FileStream.Free; end; end; procedure TExtCollection.SaveToStream(Stream: TStream); var Writer : TWriter; begin Writer:=TWriter.Create(Stream,iFilerBufferSize); Try Writer.WriteCollection(Self); Finally Writer.Free; end; end; end. |
Re: TCollection und TCollectionItem
Hi.
Mir geht es natürlich nicht um besser oder schlechter (*man kann ja nur von einander lernen*) sondern um einen ideenaustausch *g* Mir gefällt das mit der dummy-compo eigentlich ganz gut UND du müsstest auch nicht mit assign arbeiten, sondern könntest direkt die referenz zuweisen. Der vorteil wäre dann auch, das man im container noch zusätzliche properties definieren kann, die nicht in jedem item auftauchen dürfen/sollten - quasi globale infos... Was die text-konvertierung angeht, kann man es sicherlich auch so machen, das beim binären speichern direkt in den ziel-stream gespeichert wird und nur beim text-format ein puffer benutzt wird (was bei mir momentan leider nicht der fall ist). Der grosse vorteil wäre, das man die daten prüfen und editieren kann, solange man entwickelt, und wenn man das programm ausliefert, konvertiert man alles ins binär-format, womit dann jegliche redundanzen verschwinden. mfg. max. |
Re: TCollection und TCollectionItem
Zitat:
über diesen Weg die published properties der TCollection gespeichert werden. Leider bekomme ich das nicht hin. Wie meinst Du das genau? Die property Collectionname in TAddressItems wird nicht mitgespeichert :gruebel:
Delphi-Quellcode:
unit Collection;
interface uses SysUtils, classes; Type {TNumber repräsentiert je einen Eintrag in TNumbers} TNumber = class(TCollectionItem) private FNumber : Integer; public procedure Assign(Source : TPersistent); override; // muss überschrieben werden published property Number : Integer read FNumber write FNumber; end; TNumbers = class(TCollection) private function GetItem(X: Integer): TNumber; procedure SetItem(X: Integer; const Value: TNumber); public constructor Create; function Add : TNumber; property Items[X : Integer] : TNumber read GetItem write SetItem; default; end; {TAddressItem repräsentiert je einen Eintrag in TAddressItems Numbers ist hier ebenfalls ein Collection. Numbers wird automatisch gespeichert !!!} TAddressItem = class(TCollectionItem) private FFirstname : String; FNumbers : TNumbers; public constructor Create(Collection: TCollection); override; destructor Destroy; override; procedure Assign(Source : TPersistent); override; // muss überschrieben werden published property Firstname : String read FFirstname write FFirstname; property Numbers : TNumbers read FNumbers write FNumbers; end; {Das ist unsere Basisliste} TAddressItems = class(TCollection) private FCollectionName : String; function GetItem(X: Integer): TAddressItem; procedure SetItem(X: Integer; const Value: TAddressItem); public constructor Create; procedure Assign(Source : TPersistent); override; function Add : TAddressItem; procedure SaveToFile(const Filename : TFilename); procedure LoadFromFile(const Filename : TFilename); procedure SaveToStream(Stream : TStream); procedure LoadFromStream(Stream : TStream); property Items[X : Integer] : TAddressItem read GetItem write SetItem; default; published property CollectionName : String read FCollectionName write FCollectionName; end; {TAddressDummy ist ein Dummy, der nur benötigt wird, um TAddressItems zu speichern. Siehe TAddressItems.SaveToStream. Da das Streamingsystem erst ab TComponent greift brauchen wir hier diesen Dummy} TAddressDummy = class(TComponent) private FItems : TAddressItems; published property Items : TAddressItems read FItems write FItems; end; implementation { TAddressItem } procedure TAddressItem.Assign(Source: TPersistent); begin If Source is TAddressItem then begin FFirstname:=TAddressItem(Source).Firstname; FNumbers.Assign(TAddressItem(Source).Numbers); end else inherited Assign(Source); end; constructor TAddressItem.Create(Collection: TCollection); begin inherited Create(Collection); FNumbers:=TNumbers.Create; end; destructor TAddressItem.Destroy; begin FNumbers.Free; inherited Destroy; end; { TAddressItems } function TAddressItems.Add: TAddressItem; begin Result:=inherited Add as TAddressItem; end; constructor TAddressItems.Create; begin inherited Create(TAddressItem); end; function TAddressItems.GetItem(X: Integer): TAddressItem; begin Result:=inherited GetItem(X) as TAddressItem; end; procedure TAddressItems.SaveToFile(const Filename: TFilename); var FileStream : TFileStream; begin FileStream:=TFileStream.Create(Filename,fmCreate); Try SaveToStream(FileStream); Finally FileStream.Free; end; end; procedure TAddressItems.LoadFromFile(const Filename: TFilename); var FileStream : TFileStream; begin Clear; FileStream:=TFileStream.Create(Filename,fmOpenRead); Try LoadFromStream(FileStream); Finally FileStream.Free; end; end; procedure TAddressItems.SaveToStream(Stream: TStream); var AddressDummy : TAddressDummy; begin AddressDummy:=TAddressDummy.Create(Nil); Try AddressDummy.Items:=Self; Stream.WriteComponent(AddressDummy); Finally AddressDummy.Free; end; end; procedure TAddressItems.LoadFromStream(Stream: TStream); var AddressDummy : TAddressDummy; begin AddressDummy:=TAddressDummy.Create(Nil); Try AddressDummy.Items:=Self; Stream.ReadComponent(AddressDummy); Finally AddressDummy.Free; end; end; procedure TAddressItems.SetItem(X: Integer; const Value: TAddressItem); begin inherited SetItem(X,Value); end; procedure TAddressItems.Assign(Source: TPersistent); begin If Source is TAddressItems then FCollectionName:=TAddressItems(Source).CollectionName else inherited Assign(Source); end; { TNumber } procedure TNumber.Assign(Source: TPersistent); begin If Source is TNumber then begin FNumber:=TNumber(Source).Number; end else inherited Assign(Source); end; { TNumbers } function TNumbers.Add: TNumber; begin Result:=inherited Add as TNumber end; constructor TNumbers.Create; begin inherited Create(TNumber); end; function TNumbers.GetItem(X: Integer): TNumber; begin Result:=inherited GetItem(X) as TNumber; end; procedure TNumbers.SetItem(X: Integer; const Value: TNumber); begin inherited SetItem(X,Value); end; end. |
Re: TCollection und TCollectionItem
Hallo maximov,
die einzige Lösung die mir gerade eingefallen ist wäre folgende:
Delphi-Quellcode:
Aber dem Dummy ebenfalls eine Collectionname property zu spendieren finde ich irgendwie doof.
TAddressDummy = class(TComponent)
private FItems : TAddressItems; FCollectionname : String; public published property Items : TAddressItems read FItems write FItems; property Collectionname : String read FCollectionname write FCollectionname; end; procedure TAddressItems.SaveToStream(Stream: TStream); var AddressDummy : TAddressDummy; begin AddressDummy:=TAddressDummy.Create(Nil); Try AddressDummy.Items:=Self; AddressDummy.Collectionname:=FCollectionname; Stream.WriteComponent(AddressDummy); Finally AddressDummy.Free; end; end; procedure TAddressItems.LoadFromStream(Stream: TStream); var AddressDummy : TAddressDummy; begin AddressDummy:=TAddressDummy.Create(Nil); Try AddressDummy.Items:=Self; Stream.ReadComponent(AddressDummy); FCollectionname:=AddressDummy.Collectionname; Finally AddressDummy.Free; end; end; |
Re: TCollection und TCollectionItem
Zitat:
|
Re: TCollection und TCollectionItem
Hi,
wer die Sourcen hat, kann sich die Implementierung von TWebDispatcher in der Unit HTTPApp anschauen. Dort ist es so gelöst, dass a) die Collection im OI angezeigt und b) automatisch mit dem DFM gespeichert wird. mfG mirage228 |
Re: TCollection und TCollectionItem
Zitat:
@Jens:Ich seh grad, dass TCollection von TObject abstammt :( womit wir wohl deren published-props vergessen können, da sie keine RTTI besitzen...verdammt wäre ja auch zu schön gewesen! Wie wäre es mit einem 'streaming-provider' der von TComponent abgeleitet ist und standartmässig die items property hat, wo man dann soviele properties hinzufügen kann, wie man will? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:59 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz