2013-04-30 20 views
8

Mam złożony obiekt do głębokiej kopii (wiele tablic, obiektów, wskaźników, warstw warstw dziedziczenia, setki elementów różnych typów i więcej), i ponowne tworzenie go za pomocą metody Assign Delphi jest nieproduktywny i najprawdopodobniej zbyt skomplikowany.Kopiowanie obiektów w delphi

Patrzyłem na Rtti i wydaje się, że to dobra opcja, ale do tej pory nie mogłem omówić wszystkich możliwych scenariuszy. Nie chcę tracić czasu i mam nadzieję znaleźć dobry i prosty przykład. Niestety, nie mogłem go jeszcze znaleźć. Co robiłem do tej pory, przechodzę przez cały obiekt z pętlą (TRttiType.GetFields()) i próbuję przypisać wszystko za pomocą wskaźników opartych na wartościach TTypeKind. (tkPointer, tkClass, tkClassRef ...)

Znalazłem przykład JSON/Marshalling, ale nie mogłem głęboko skopiować mojego złożonego obiektu; Mam błąd;

Wewnętrzne: Rodzaj tkPointer nie jest obecnie obsługiwana

http://www.yanniel.info/2012/02/deep-copy-clone-object-delphi.html

Czy jest coś w Delphi blisko C# szeregowania binarny i tworząc głęboką kopię za pomocą strumienia pamięci. Czy istnieje dobry i prosty przykład, o którym wiesz w Delphi, wykonując głęboką kopię z RTTI lub JSON/Marshalling, które działałyby z najbardziej złożonymi obiektami?

+0

Jerry, ta klasa dziedziczy już TPersistent, a funkcja Assign jest nadpisana. Musiałbym ręcznie przypisać sobie wzajemnie setki obiektów, chyba że jest to automatyczny sposób. (Próbowałem wywoływać dziedziczony program Assign, który spowodował błąd w stylu "Nie można przypisać MyObject do MyObject", co miało miejsce, mimo że sprawdzałem poprawny typ obiektu przed wywołaniem przypisania.) – Alex

+3

Setki członków? Wygląda na to, że musisz złagodzić tego złego chłopca. Na to, co jest warte, są setki pytań o przetrwanie na SO. Wiele odpowiedzi już tam jest. –

+2

Nie, 'Assign' nie działa w ten sposób. ** Musisz ** nadpisać 'AssignTo' i podać średnią dla kopiowania – OnTheFly

Odpowiedz

5

W kilku słowach Ci nie można używać RTTI uprościć głęboka kopia (będzie to o wiele bardziej skomplikowane i podatne na błędy niż stosując klasyczną nadpisanie Przypisz)

więc trzeba spojrzeć bliżej TPersistent i jego obiekty podrzędne i prawidłowo przesłonić Przypisz, AssignTo metod (nie ma prostszy sposób)

0

Alex miałem ten sam problem jak ty, ja złamałem nieco głowę i napisał następujący kod, który odpowiedział na moje problem, pełen nadziei ly również spotkać się z tobą lub z innymi.

function TModel.Clone(pObj:TObject): TObject; 
procedure WriteInField(pField:TRttiField; result, source:Pointer); 
var 
    Field:TRttiField; 
    Val:TValue; 
    Len, I :Integer; 
    tp:TRttiType; 
    ctx:TRttiContext; 
begin 
    if not pField.GetValue(source).IsEmpty then 
    case pField.FieldType.TypeKind of 
     TTypeKind.tkRecord: 
     begin 
      for Field in pField.FieldType.GetFields do 
      WriteInField(Field, PByte(result)+pField.Offset, pField.GetValue(source).GetReferenceToRawData); 
     end; 
     TTypeKind.tkClass: 
     begin 
      Val:=Self.Clone(pField.GetValue(source).AsObject); 
      if Assigned(TObject(pField.GetValue(result).AsObject)) then 
      pField.GetValue(result).AsObject.Free; 

      pField.SetValue(result,Val); 
     end; 
     TTypeKind.tkDynArray: 
     begin 
      Len := pField.GetValue(source).GetArrayLength; 
      for I := 0 to Len -1 do 
      case pField.GetValue(source).GetArrayElement(I).Kind of 
       TTypeKind.tkRecord: 
       begin 
       tp:=ctx.GetType(pField.GetValue(source).GetArrayElement(I).TypeInfo); 
       for Field in tp.GetFields do 
        WriteInField(Field,PByte(result)+Field.Offset, pField.GetValue(source).GetReferenceToRawData); 

       end; 
       TTypeKind.tkClass: 
       begin 
       Val:=Self.Clone(pField.GetValue(source).GetArrayElement(I).AsObject); 
       DynArraySetLength(PPointer(PByte(result)+pField.Offset)^,pField.GetValue(source).TypeInfo,1,@Len); 
       pField.GetValue(result).SetArrayElement(I,Val); 
       end; 
      else 
       DynArraySetLength(PPointer(PByte(result)+pField.Offset)^,pField.GetValue(source).TypeInfo,1,@Len); 
       pField.GetValue(result).SetArrayElement(I, pField.GetValue(source).GetArrayElement(I)); 
      end; 

     end; 
    else 
     pField.SetValue(result,pField.GetValue(source)); 
    end; 
end; 
var 
    Context: TRttiContext; 
    IsComponent, LookOutForNameProp: Boolean; 
    RttiType: TRttiType; 
    Method: TRttiMethod; 
    MinVisibility: TMemberVisibility; 
    Params: TArray<TRttiParameter>; 
    PropFild: TRttiField; 
    Fild: TRttiField; 
    SourceAsPointer, ResultAsPointer: Pointer; 
    ObjWithData:TObject; 
    Value:TValue; 

begin 
try 
    if Assigned(pObj) then 
    ObjWithData := pObj 
    else 
    ObjWithData := Self; 
    RttiType := Context.GetType(ObjWithData.ClassType); 
    //find a suitable constructor, though treat components specially 
    IsComponent := (ObjWithData is TComponent); 
    for Method in RttiType.GetMethods do 
    if Method.IsConstructor then 
    begin 
     Params := Method.GetParameters; 
     if Params = nil then Break; 
     if (Length(Params) = 1) and IsComponent and 
     (Params[0].ParamType is TRttiInstanceType) and 
     SameText(Method.Name, 'Create') then Break; 
    end; 
    if Params = nil then 
    Result := Method.Invoke(ObjWithData.ClassType, []).AsObject 
    else 
    Raise Exception.CreateFmt('Object Invalid to clone : ''%s''', [ObjWithData.ClassName]); 
    try 

    //loop through the props, copying values across for ones that are read/write 
    Move(ObjWithData, SourceAsPointer, SizeOf(Pointer)); 
    Move(Result, ResultAsPointer, SizeOf(Pointer)); 
    for PropFild in RttiType.GetFields do 
     WriteInField(PropFild,ResultAsPointer,SourceAsPointer); 

    except 
    Result.Free; 
    raise; 
    end; 
finally 
    ObjWithData := nil; 
end; 

end;