| Autor |
Beitrag |
Bayo
      
Beiträge: 39
|
Verfasst: Mi 04.06.03 11:33
Hi zusammen
Ich habe ein Objekt und möchte gerne eine Kopie dieses Objekts erstellen! Mein Code um dieses Problem zu lösen sieht auf das wesentliche beschränkt folgendermassen aus:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51:
| procedure Test; var obj1: TButton; obj2: TControl; begin obj2 := TControlClass(obj1.ClassType).Create(nil); CloneAttributes(obj1, obj2); ... FreeAndNil(obj2); end;
procedure CloneAttributes(master, addicted: TObject); var count, size: Integer; propList: PPropList; i: Integer;
propValue: String; methPropValue: TMethod; objPropValue: TObject; const kinds: TTypeKinds = [tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod]; begin if not (master = nil) and not (addicted = nil) then begin if master.ClassType = addicted.ClassType then begin count := GetPropList(master.ClassInfo, kinds, nil); size := count * SizeOf(Pointer); GetMem(propList, size); GetPropList(master.ClassInfo, kinds, propList); for i:=0 to count-1 do begin if IsPublishedProp(master, PropList[i].Name) then begin case PropList[i].PropType^.Kind of tkClass: begin objPropValue := GetObjectProp(master, PropList[i].Name); SetObjectProp(addicted, PropList[i].Name, objPropValue); end; tkMethod: begin methPropValue := GetMethodProp(master, PropList[i].Name); SetMethodProp(addicted, propList[i].Name, methPropValue); end; else propValue := GetPropValue(master, PropList[i].Name); SetPropValue(addicted, propList[i].Name, propValue); end; end; end; FreeMem(propList, size); end; end; end; |
Funktioniert grundsätzlich auch, nur: Properties vom Typ tkClass können auf zwei verschiedene Arten vorkommen. Entweder ist das Objekte einfach im ObjectInspektor zugewiesen (z. B. PopupMenu)(Assiziation) oder das Objekte wird selbst kreiert und wieder freigegeben (z. B. Font) (Komposition)! Wenn ich das Property des abhängigen controls setze: SetObjectProp, soll es bei einer Assoziation den Zeiger ändern, nicht aber bei einer Komposition! In den meisten Fällen wird das von der Funktion SetObjectProp automatisch gemacht, aber nicht in allen! Wird bei einer Komposition der Zeiger auf das Objekte des masters geändert, entsteht 1. ein Speicher-Loch und 2. gibt es eine Zugriffsverletzung solbad das zweite Objekte aus dem Speicher entfernt wird! Weiss evtl. jemand wie Delphi entscheidet, ob der Zeiger geändert wird oder nicht?
Ich freue mich über jede Antwort!
Grüsse Dominic
Moderiert von UGrohne: Code- durch Delphi-Tags ersetzt
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Mi 04.06.03 12:33
hi...Glaub ich hab dein prob. verstanden.
vom objekt selbst erstellte objekte werden vom objekt auch wieder gelöscht. wenn du jetzt einfach den pointer 'clonst' dann zeigen zwei objekte (properties) auf das selbe obj. Beim 'destroyen' des einen obj, zeigt das andere noch da druff...desweiteren hast du ein speicher-leck, weil das unter-obj ja von beiden erstellt wurde, aber beide nur auf eines zeigen? hab ich das richtig verstanden?
Wenn ja dann könntest du doch prüfen, wenn du eine tkClass-prop. kopieren willst, ob die ziel prop <> nil ist und wenn dem so ist, dann clonst du diese beiden auch, sodass beide unter-objs existieren bleiben. Wenn besagte prop nil ist, dann wurde es auch nicht automatisch erstellt und du hast >theoretisch< keine probleme bein FREEen.
macht das sinn?
mfg maximus
PS: zum klonen der attribute ist eigentlich die methode 'assign' vorgesehen, sie ist allerdings nicht in allen classen überschrieben.
_________________ mfg.
mâximôv
|
|
Bayo 
      
Beiträge: 39
|
Verfasst: Mi 04.06.03 13:50
Du hast das Problem genau richtige verstanden! Ein Objekte wird durch das Ändern des Zeigers nicht freigegeben und das zweite doppelt, was zu einer Zugriffsverletzung führt!
Natürlich ist eigentlich Assign vorgesehen für diese Aufgabe! Vielfach ist diese Methode aber nicht oder nicht wie gewünscht überschrieben  Wie auch in meinem Fall (es geht nicht effektiv um ein TButton-Objekt, wie im Beispiel)
Dein Ansatz ist gut! Rekursiver aufruf der Clone-Funktion  Müsste funktionieren solange das abhängige Objekte direkt nach dem krieren "geklont" wird! Ist diesem Objekt aber ein drittes Objket Assoziiere, würde sich dieses auch verändern! Das ist für mein Problem nicht relevant, somit sehe ich das als Notlösung(Vielen Dank  )! Wäre natürlich aber schön, wenn ich das Problem allgemein lösen könnte!
Delphi kennt einen Unterschied zwischen Assoziationen und Kompositionen, er wird in der Funktion SetObjectProp berücksichtigt! Ich weiss aber nicht wie und warum (leider kann ich kein Assambler  )! Und vor allem wird er nicht in jedem fall berücksichtigt!
Der geänderte Code:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| tkClass: begin objPropValueAc := GetObjectProp(addicted, PropList[i].Name); objPropValue := GetObjectProp(master, PropList[i].Name); if objPropValueAc = nil then SetObjectProp(addicted, PropList[i].Name, objPropValue) else CloneAttributes(objPropValue, objPropValueAc); end; |
Ich werde den fertigen Code hier Posten, sobald ich ihn habe und sofern ich das Problem allgemein lösen kann!
Vielen Dank maximus... Dominic
Moderiert von Tino: Code- durch Delphi-Tags ersetzt.
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Mi 04.06.03 14:18
| Zitat: | Delphi kennt einen Unterschied zwischen Assoziationen und Kompositionen, er wird in der Funktion SetObjectProp berücksichtigt! Ich weiss aber nicht wie und warum (leider kann ich kein Assambler )! Und vor allem wird er nicht in jedem fall berücksichtigt!
|
^
Was genau meinst du mit 'Assoziationen und Kompositionen'?
_________________ mfg.
mâximôv
|
|
Bayo 
      
Beiträge: 39
|
Verfasst: Mi 04.06.03 15:52
Hm ja, dass wäre evtl. noch wichtig zu erläutern... i'm sorry... also:
Assotiation, Aggregation, Komposition und Dependency bezeichnen Bezeichnungen zwischen Objekten. Man braucht diese Begriffe, wenn man Klassendiagramme gemäss UML erstellt:
Assotiation: Ein Objekt1 kennt Objekt2, ist aber nicht verantwortlich für das erstellen bzw. Freigeben von Objekt2.
Aggregation: Objekt2 ist gehört zu Objekt1. Wird Objekt1 freigegeben, exestiert Objekt1 aber weiter.
Komposition: Objekt2 ist Teil vom Objekt1 und existiert nur solange Objekt1 existiert.
Dependency: Objekt2 stellt Funktionen zur verfügung, die von Objekt1 evtl. gebraucht werden. Die beiden Objekte haben aber keinen direkten zusammenhang (ist eigentlich eine spezielle Assotiation, die gemäss UML aber unterschieden wird!)
Grüsse Dominic
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Mi 04.06.03 17:16
Aha...wieder was gelernt
Aber letztendlich sind die beziehungen doch nur im code verankert...will meinen, dass es keine flags gibt die darüber rechenschaft ablegen. oder? ...ich würde jetzt raten, dass man auf die beziehung nur indirekt schliessen kann.
_________________ mfg.
mâximôv
|
|
Bayo 
      
Beiträge: 39
|
Verfasst: Mi 04.06.03 17:50
Yep, da hast du sicherlich richtig geraten! Es ist aber eine Tatsache, dass sich Delphi bei der Funktion SetObjectProp unterschiedlich verhält. Entweder werden Objekteigenschaften kopiert oder der Zeiger wird umgehängkt. Ich weiss eifach nicht WIE Delphi indirekt auf diese Beziehung schliesst  Egal! Dein Tipp mit dem Nil hat mir schon einiges gebracht!
Folgend findet ihr die fertige Funktion um ein beliebiges Objkt zu clonen, bei der es folgendes zu beachten gibt:
Beim Setzen eines Properties wird der jeweilige Setter ausgeführt(könnte man evtl. umgehen!)! Gibt es im Setter eine Exception, wird diese NICHT behandelt, die Eigenschaft also auch nicht gesetzt!
Es werden ausschlisslich Published-Properties "geklont", was zur Folge hat, dass z. B. der Parent nicht gesetzt wird!
Objekt-eigenschaften die im addicted-Objekt Nil sind werden vom master-Objekt übernommen, was bei einer Assoziation (siehe oben  ) richtig ist, was bei einer Komposition unter Umständen(wenn der Zeiger umgehängkt wird) zu einem Fehler führt!
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87:
| procedure CloneAttributes(master, addicted: TObject; newName: String = ''); var count, size: Integer; propList: PPropList; i: Integer;
masterObj: TObject; addictedObj: TObject; compName: String; compIdx: Integer;
const kinds: TTypeKinds = [tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString, tkInt64]; begin if not (master = nil) and not (addicted = nil) then begin if master.ClassType = addicted.ClassType then begin count := GetPropList(master.ClassInfo, kinds, nil); size := count * SizeOf(Pointer); GetMem(propList, size); GetPropList(master.ClassInfo, kinds, propList); for i:=0 to count-1 do begin if IsPublishedProp(master, PropList[i].Name) then begin try case PropList[i].PropType^.Kind of tkString, tkLString, tkWString: begin try SetStrProp(addicted, PropList[i].Name, GetStrProp(master, PropList[i].Name)); except on EComponentError do begin if LowerCase(PropList[i].Name) = 'name' then begin compName := newName; compIdx := 0; if compName = '' then begin compName := GetStrProp(master, PropList[i].Name); compIdx := 1; end; while True do begin try if compIdx = 0 then SetStrProp(addicted, PropList[i].Name, compName) else SetStrProp(addicted, PropList[i].Name, compName+IntToStr(compIdx)); Break; except inc(compIdx); end; end; end; end; end; end; tkEnumeration: begin SetEnumProp(addicted, PropList[i].Name, GetEnumProp(master, PropList[i].Name)); end; tkInteger, tkChar, tkWChar, tkSet: begin SetOrdProp(addicted, PropList[i].Name, GetOrdProp(master, PropList[i]. Name)); end; tkFloat: begin SetFloatProp(addicted, PropList[i].Name, GetFloatProp(master, PropList[i].Name)); end; tkInt64: begin SetInt64Prop(addicted, PropList[i].Name, GetInt64Prop(master, PropList[i].Name)); end; tkMethod: begin SetMethodProp(addicted, PropList[i], GetMethodProp(master, PropList[i].Name)) end; tkClass: begin masterObj := GetObjectProp(master, PropList[i].Name); addictedObj := GetObjectProp(addicted, PropList[i].Name); if addictedObj = nil then begin SetObjectProp(addicted, PropList[i].Name, masterObj); end else begin CloneAttributes(masterObj, addictedObj); end; end; end; except end; end; end; FreeMem(propList, size); end; end; end; |
Nochmals vielen Dank für deine Bemühungen!
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Do 05.06.03 10:50
Gern geschehen 
_________________ mfg.
mâximôv
|
|
|