Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi TCollection und TCollectionItem (https://www.delphipraxis.net/22549-tcollection-und-tcollectionitem.html)

Jens Schumann 19. Mai 2004 07:45


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.

maximov 23. Mai 2004 00:45

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: http://www.delphi-forum.de/viewtopic.php?t=18605

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.

Jens Schumann 23. Mai 2004 07:23

Re: TCollection und TCollectionItem
 
@maximov: GUter Tip - werde ich mir mal reinziehen.

Jens Schumann 26. Mai 2004 13:26

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.

maximov 26. Mai 2004 13:55

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.

Jens Schumann 26. Mai 2004 16:46

Re: TCollection und TCollectionItem
 
Zitat:

Zitat von maximov
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...

:gruebel: Wenn ich es richtig verstehe, sollen
ü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.

Jens Schumann 26. Mai 2004 16:56

Re: TCollection und TCollectionItem
 
Hallo maximov,
die einzige Lösung die mir gerade eingefallen ist wäre folgende:
Delphi-Quellcode:
   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;
Aber dem Dummy ebenfalls eine Collectionname property zu spendieren finde ich irgendwie doof.

maximov 27. Mai 2004 00:43

Re: TCollection und TCollectionItem
 
Zitat:

Zitat von Jens Schumann
Zitat:

Zitat von maximov
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...

:gruebel: Wenn ich es richtig verstehe, sollen
ü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:
....

Ja du hast es erfasst! Das ist in der tat ein problem, da das streaming-system die collection-klasse gesondert behandelt und es somit nur als container für items identifiziert :? Aber dazu fällt mir bestimmt noch was ein :stupid: ...wär halt cool weil man damit das klassische array problem aufbrechen könnten und somit nicht nur serielle daten des gleichen typs permanent verfügbar machen könnte! Mann kann natürlich gleich ein TComponent als ausgangspunkt nehmen, was aber nicht halb so elegant wäre, da sich damit ja sowiso hierarchische strukturen streamen lassen :wink: ...mal sehn

mirage228 27. Mai 2004 12:34

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

maximov 27. Mai 2004 13:37

Re: TCollection und TCollectionItem
 
Zitat:

Zitat von mirage228
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

Dazu muss man kein prophet sein :mrgreen: ...dafür sind collections ja auch da, wir versuchen nur grad zu klären, ob wir sie für unsere eigenen, dunklen zwecke missbrauchen können, bzw. erweitern und das mit möglichst hoher informations-effiziens und service-fähigkeiten. :stupid:

@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 06:58 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz