Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Kopie eines Klassenobjekts erstellen (https://www.delphipraxis.net/200078-kopie-eines-klassenobjekts-erstellen.html)

DieDolly 17. Mär 2019 19:01

Kopie eines Klassenobjekts erstellen
 
Zu aller erst möchte ich auf diese Seite verweisen auf die ich bei meiner Googlesuche gestoßen bin.
http://forums.devshed.com/delphi-pro...le-449161.html

Da habe ich jetzt die Info her, dass man mit Assign() eine Kopie von TPersistent-Objekten erstellen kann, es sei denn man hat eine eigene Klassenimplementierung / ein eigenes TObject.
Solche Objekte haben keine Assign-Methodik und man muss sie selber implementieren.

Ich konnte in der VCL keinen Code finden der mir eine Art Grundlage gibt, auf die ich aufbauen kann.
Könnt ihr mir helfen?

Mein Klassenobjekt ist so deklariert
Delphi-Quellcode:
TMyObject= class(TObject)
Oder ist es am einfachsten von TPersistent abzuleiten statt von TObject?

dummzeuch 17. Mär 2019 19:14

AW: Kopie eines Klassenobjekts erstellen
 
Beispiele sind TFont oder TStringList.

haentschman 18. Mär 2019 06:47

AW: Kopie eines Klassenobjekts erstellen
 
Moin...:P
Delphi-Quellcode:
class function TSEAMToolsJson.ObjectCopy(aValue: TObject): TObject;
var
  MarshalObj: TJSONMarshal;
  UnMarshalObj: TJSONUnMarshal;
  JSONValue: TJSONValue;
begin
  Result := nil;
  MarshalObj := TJSONMarshal.Create;
  try
    UnMarshalObj := TJSONUnMarshal.Create;
    try
      JSONValue := MarshalObj.Marshal(aValue);
      try
        if Assigned(JSONValue) then
          Result := UnMarshalObj.Unmarshal(JSONValue);
      finally
        JSONValue.Free;
      end;
    finally
      UnMarshalObj.Free;
    end;
  finally
    MarshalObj.Free;
  end;
end;
...fertsch. :P

Hinweis: bei Problemen siehe https://www.delphipraxis.net/199022-...agedialog.html

Sherlock 18. Mär 2019 08:52

AW: Kopie eines Klassenobjekts erstellen
 
Und wie performant ist das mit dem JSON hin und her konvertieren?
Es sieht allerdings deutlich aufgeräumter aus als
Delphi-Quellcode:
procedure TCTGNote.Assign(Source: TCTGNote);
begin
  if Assigned(Source) and (Source is TCTGNote) then
  begin
    Self.fID := Source.fID;
    Self.fTraceID := Source.fTraceID;
    Self.fEntryDate := Source.fEntryDate;
    Self.fNote := Source.fNote;
    Self.fUserName := Source.fUserName;
    Self.fUserID := Source.fUserID;
    Self.fNoteType := Source.fNoteType;
  end;
end;
Sherlock

haentschman 18. Mär 2019 08:57

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Es sieht allerdings deutlich aufgeräumter aus als
..erst Recht mit Datenobjekten mit mehr als 100 Properties. :P

Stevie 18. Mär 2019 09:44

AW: Kopie eines Klassenobjekts erstellen
 
Brrr, bloß nich über JSON, das wird doch jede zweite Version erstmal für 1 oder 2 Updates kaputt gemacht...

TiGü 18. Mär 2019 10:06

AW: Kopie eines Klassenobjekts erstellen
 
Wenn überschaubare Anzahl an Feldern nehme eine eigene Methode zum Kopieren.

Vorteil:
- am Performantesten
- Prüfungen zur Laufzeit möglich
- Debugging ist einfach

Nachteil:
- ggf. viel Quelltext

Wenn der Aufwand für viele Felder zu hoch ist, dann nehme einen Ansatz per RTTI (Google: "delphi copy object rtti").

QuickAndDirty 18. Mär 2019 10:13

AW: Kopie eines Klassenobjekts erstellen
 
@OP
Wenn man eine eigenes Objekt kopierbar habe will implemtiert man die Methoden Assign und AssignTo selbst.
Wenn man ein Objekt von TPersistent ableitet muss man Methoden zum Speichern(als stream/Datei) und Lesen(von Stream/Datei) des Objekts implemtieren.

Es ist mit hilfe der sogenannten RTTI und vor allem der Advanced-RTTI Unterstützung aktueller Delphi Versionen möglich eine Objekt-Kopier-Funktion zu schreiben, welche ziemlich viele Arten von einfachen Objekten kopiert.

Es gibt dabei natürlich einiges zu bedenken! Wenn Objekte welche ihrerseits Objekte Referenzieren kopiert werden sollen muss geklärt sein ob es sich bei diesen Referenzen um Assoziationen oder Compositionen handelt. Da RTTI nicht preisgibt ob ein Attribut vom Typ Objekt-Referenz durch eine Methode dieses Objekts erzeugt und zerstört wird.

Es bedürfte also eines Markers zur Unterscheidung von Assoziation und Composition im Aufbau der Klassen...und das wird bei den Mitgelieferten klassen nicht bereits unterschieden. Kann aber für eigene klassen umgesetzt werden.

Klassen die reine Name-Wert-Paare sind lassen sich am leichtesten über RTTI kopieren.

Leider bläht RTTI das Kompilat auf! Und man muss manchmal dafür sorgen das RTTI für eigene Klassen global zur Verfügung stehen.
Bei Komplexeren klassen muss man evtl. beim kopieren auf zirkuläre referenzen prüfen (siehe -> durchsuchen eines Graphen aka DFS vs BFS)

DieDolly 18. Mär 2019 11:06

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Wenn man eine eigenes Objekt kopierbar habe will implemtiert man die Methoden Assign und AssignTo selbst.
Wenn man ein Objekt von TPersistent ableitet muss man Methoden zum Speichern(als stream/Datei) und Lesen(von Stream/Datei) des Objekts implemtieren.
Die Daten lade und speichere ich eh selber mit extra Quelltext. Was anderes käme mir gar nicht in den Sinn.

Ist von TPersistent ableiten also gut genug und genau so perfomant wie TObject? Es geht hier um das Erstellen von circa 150 bis 200 Klassenobjekten bei Programmstart.
So wie ich das sehe leitet TPersistent am Ende auch wieder nur von TObject ab und fügt 7 neue Prozeduren und Funktionen ein.

Komplizierte Objekttypen habe ich nicht. Strings, Integer und eindimensionale Arrays (String, Integer und einfache Enums).

Stevie 18. Mär 2019 11:09

AW: Kopie eines Klassenobjekts erstellen
 
Wenn die Klassen, die kopiert werden sollen, so umfangreich und zahlreich werden, kann man auch Code generieren - entweder durch Tools wie DelphiAST, oder man packt die Klassen in ein Projekt, in denen das anhand deren RTTI passiert. Dann hat man die Vorteile von beiden Herangehensweisen.

DieDolly 18. Mär 2019 11:10

AW: Kopie eines Klassenobjekts erstellen
 
Verstanden was du da gerade geschrieben hast, habe ich leider nicht :pale:

Stellen 100 Properties denn ein Problem dar?

Speicherverbrauch meiner Anwendung mit und ohne Klassenobjekten im Detail
mit 0 Klassenobjekten bei Programmstart 9,2 MB
mit 70 Klassenobjekten 10,7 MB.

Stevie 18. Mär 2019 11:30

AW: Kopie eines Klassenobjekts erstellen
 
Meine Aussage bezog sich auf den Post von QuickAndDirty zuvor

Andreas L. 18. Mär 2019 11:43

AW: Kopie eines Klassenobjekts erstellen
 
Ich mach es z. B. so:

Delphi-Quellcode:
  TtoTreeNodeAttribute = class(TtoCollectionItem)
  private
    FData: TtoData;
    FName: String;
    ...
  protected
    procedure SetName(Value: String);
    procedure SetData(Value: TtoData);
    ...
  public
    ...
    procedure AssignTo(Dest: TPersistent); override;
  published
    property Name: String read FName write SetName;
    property Data: TtoData read FData write SetData;
    ...
  end;

...

procedure TtoTreeNodeAttribute.AssignTo(Dest: TPersistent);
var
  DestObj: TtoTreeNodeAttribute;
begin
  inherited AssignTo(Dest);
  if Dest is TtoTreeNodeAttribute then
  begin
    DestObj := TtoTreeNodeAttribute(Dest);
    DestObj.BeginUpdate;
    try
      DestObj.FData.Assign(FData);
      DestObj.FName := FName;
      ...
    finally
      DestObj.EndUpdate;
    end;
  end;
end;

procedure TtoTreeNodeAttribute.SetName(Value: String);
begin
  if (FName <> Value) and (Value <> '') then
    FName := Value;
end;

procedure TtoTreeNodeAttribute.SetData(Value: TtoData);
begin
  FData.Assign(Value);
end;

...
Anmerkung: Die Methoden BeginUpdate() und EndUpdate() habe ich selbst implementiert weil es sie in in TPersistent nicht gibt. (Delphi 2009)

DieDolly 18. Mär 2019 11:50

AW: Kopie eines Klassenobjekts erstellen
 
Delphi-Quellcode:
DestObj.FData.Assign(FData);
DestObj.FName := FName;
Bei ein paar Properties kein Problem. Bei alles über 50 aufwendig und doppelter Aufwand, wenn irgendwas geändert werden muss. Mal von der Gefahr abgesehen, wenn man vergisst AssignTo ebenfalls zu erweitern wenn man die Klasse erweitert.

peterbelow 18. Mär 2019 12:08

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Zitat von DieDolly (Beitrag 1427976)
Zitat:

Wenn man eine eigenes Objekt kopierbar habe will implemtiert man die Methoden Assign und AssignTo selbst.
Wenn man ein Objekt von TPersistent ableitet muss man Methoden zum Speichern(als stream/Datei) und Lesen(von Stream/Datei) des Objekts implemtieren.
Die Daten lade und speichere ich eh selber mit extra Quelltext. Was anderes käme mir gar nicht in den Sinn.

Ist von TPersistent ableiten also gut genug und genau so perfomant wie TObject? Es geht hier um das Erstellen von circa 150 bis 200 Klassenobjekten bei Programmstart.
So wie ich das sehe leitet TPersistent am Ende auch wieder nur von TObject ab und fügt 7 neue Prozeduren und Funktionen ein.

Komplizierte Objekttypen habe ich nicht. Strings, Integer und eindimensionale Arrays (String, Integer und einfache Enums).

Egal von was Du ableitest, den Code für das Kopieren der Daten mußt Du auf jeden Fall schreiben. Assign/AssignTo in TPersistent sind nur Platzhalter, die eine Exception auslösen, wenn Du nicht mindestens AssignTo überschreibst. Es hängt also von Dir ab, wie performant das Ganze ist :wink: . TPersistent enthält einiges an Kode für die Unterstützung des VCL Streamingmechanismusses, aber das belasted Abkömmlinge nicht wesentlich. Trotzdem würde ich persönlich nicht von einer Klasse ableiten, deren Funktionalität Du nicht wirklich brauchst. Ein Argument für TPersistent wäre allerdings, wenn Du z. B. Assign und AssignTo so implementieren willst, dass der Inhalt eines deiner Objekte auch z. B. per Assign in eine TStringlist oder TMemo.Lines oder so kopiert werden können soll.

DieDolly 18. Mär 2019 12:13

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Egal von was Du ableitest, den Code für das Kopieren der Daten mußt Du auf jeden Fall schreiben. Assign/AssignTo in TPersistent sind nur Platzhalter, die eine Exception auslösen, wenn Du nicht mindestens AssignTo überschreibst.
Komisch. Ich leite gerade von TPersistent ab, Mache LokalObject.Assign(PublicObject); und alles funktioniert ohne exception.

Zitat:

Ein Argument für TPersistent wäre allerdings, wenn Du z. B. Assign und AssignTo so implementieren willst, dass der Inhalt eines deiner Objekte auch z. B. per Assign in eine TStringlist oder TMemo.Lines oder so kopiert werden können soll.
Brauche ich zum jetzigen Zeitpunkt nicht. Zum jetzigen Zeitpunkt kopiere ich nur komplette Klassenobjekte.

Sonst, wenn das besser ist, nehme ich Tigüs Idee ( https://delphihaven.wordpress.com/20...ng-using-rtti/ ).
Das mit Json lasse ich wegen dem Argument, dass Embarcadero das ja oft kaputt macht.,

Zitat:

Trotzdem würde ich persönlich nicht von einer Klasse ableiten, deren Funktionalität Du nicht wirklich brauchst.
Hat das einen Nachteil? Außer, dass ich Funktionen in der Autovervollständigung habe, die ich vielleicht nicht brauche?

TiGü 18. Mär 2019 13:37

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Zitat von DieDolly (Beitrag 1427990)
Zitat:

Trotzdem würde ich persönlich nicht von einer Klasse ableiten, deren Funktionalität Du nicht wirklich brauchst.
Hat das einen Nachteil? Außer, dass ich Funktionen in der Autovervollständigung habe, die ich vielleicht nicht brauche?

Objekte werden größer (SizeOf()).

DieDolly 18. Mär 2019 13:44

AW: Kopie eines Klassenobjekts erstellen
 
Laut Windows Process Explorer, direkt nach Programmstart wenn alles geladen wurde:
TObject, private bytes 19.704 K, working set 31,644 K
TPersistent, private bytes 19.648 K, working set 31.536 K

Es gibt sicherlich einen anderen Unterschied irgendwo aber der scheint bei mir noch so klein zu sein, dass man ihn nicht merkt.
Die paar Byte sind heute eh egal und auf die kommts auch nicht an bei einer Speicherreservierung von rund 30 MB.

Ich habe das jetzt auch mal was genauer gemacht. Ob das richtig ist, weiß ich aber nicht.
Ich bin alle Objekte durchgegangen, habe deren Größe mit InstanceSize bestimmt, addiert und am Ende ausgegeben.
Für TPersistent bekomme ich 22680, für TObject auch 22680.

Es geht mir keineswegs darum ein paar bytes zu sparen. Ich frage mich nur, warum man TObject mit eigenem Assign, was vielleicht sogar noch anfällig ist, nehmen sollte, obwohl TPersistent doch schon alles liefert.

Stevie 18. Mär 2019 14:08

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Zitat von TiGü (Beitrag 1428000)
Objekte werden größer (SizeOf()).

Nö, TPersistent hat keine neuen Felder, sondern nur 3 virtuelle und 2 dynamischen Methoden.

Zitat:

Zitat von DieDolly (Beitrag 1428002)
Ich frage mich nur, warum man TObject mit eigenem Assign, was vielleicht sogar noch anfällig ist, nehmen sollte, obwohl TPersistent doch schon alles liefert.

Einziger Grund, warum ich das machen würde, wäre, dass ich dann nen virtueller Konstruktor auch einbauen könnte, so dass ich über die Metaklasse neue Objekte erzeugen kann.

DieDolly 18. Mär 2019 14:10

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Nö, TPersistent hat keine neuen Felder, sondern nur 3 virtuelle und 2 dynamischen Methoden.
Ist zwar nicht an mich gerichtet, aber trotzdem.

Dann deckt sich das ja mit meinem Fund
Zitat:

Für TPersistent bekomme ich 22680, für TObject auch 22680.

TiGü 18. Mär 2019 14:22

AW: Kopie eines Klassenobjekts erstellen
 
Na, irgendwo in der VMT muss es ja hin. Ja, es geht hier nur um Bytes. Ja, einzelne Bytes, die man an 0010 Händen abzählen kann.

DieDolly 18. Mär 2019 14:24

AW: Kopie eines Klassenobjekts erstellen
 
Manchmal wünschte ich mir, dass man heute selbst noch auf Bytes achtet und die nicht verschwendet. Aber jetzt bei all den Gigabytes an Arbeitsspeicher und Unmengen an CPU-Caches interessiert das leider niemanden mehr - mich eingeschlossen. Das würde einfach zuviel Zeit kosten die paar Bytes zu optimieren.

Stevie 18. Mär 2019 14:42

AW: Kopie eines Klassenobjekts erstellen
 
Zitat:

Zitat von DieDolly (Beitrag 1428007)
Manchmal wünschte ich mir, dass man heute selbst noch auf Bytes achtet und die nicht verschwendet. Aber jetzt bei all den Gigabytes an Arbeitsspeicher und Unmengen an CPU-Caches interessiert das leider niemanden mehr - mich eingeschlossen. Das würde einfach zuviel Zeit kosten die paar Bytes zu optimieren.

Jein, aber man muss auch wissen, an welchen Ecken man spart.
Wenn die RTL aber schon den CPU Cache mit Müll zupflastert, dann kann man sich auch eigene Microoptimierungen an den Hut stecken ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:39 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 by Thomas Breitkreuz