2013-04-30 64 views
8

我有一个复杂的对象深复制(大量的数组,对象,指针,继承层,数百名各种类型的成员和更多),并通过Delphi的Assign方法重新创建它是没有生产力,很可能太复杂。对象复制在delphi

我一直在寻找Rtti,它似乎是一个很好的选择,但到目前为止我无法覆盖所有可能的情况。我不想浪费那么多时间,希望找到一个好的简单例子。不幸的是,我还找不到。到目前为止,我一直在做的是,在对象中使用循环(TRttiType.GetFields())遍历所有TRttiField,并尝试使用基于TTypeKind值的指针分配所有内容。 (tkPointer,tkClass,tkClassRef ...)

我找到了JSON /编组示例,但它无法深度复制我的复杂对象;我有错误;

内部:类型tkPointer目前不支持

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

是否有德尔福接近C#二进制序列化和使用内存流创建深拷贝任何东西。还是有一个很好的简单的例子,你知道在Delphi中使用RTTI或JSON/Marshalling进行深层复制,可以处理最复杂的对象?

+0

杰里,这个类已经继承了TPersistent,并且Assign被覆盖。我必须手动分配数百个对象,除非有自动的方式。 (我试过调用继承的Assign,并且抛出了一个类似“无法将MyObject分配给MyObject”的错误,即使我在调用Assign之前检查了正确的对象类型,也是如此。) – Alex 2013-04-30 20:28:44

+3

数百个成员?听起来你需要把这个坏男孩瘦下来。对于什么是值得的,这里有数百个持续性问题。已经有很多答案了。 – 2013-04-30 21:23:47

+2

不,“分配”不能以这种方式工作。 **您**应该覆盖'AssignTo'并提供复制的意思 – OnTheFly 2013-04-30 22:09:31

回答

5

几句话,你不能使用RTTI来简化深拷贝(这将是一个方式比使用传统的越权分配更加复杂,容易出错)

所以你需要寻找更接近TPersistent及其子对象,妥善覆盖分配AssignTo方法(有没有更简单的方法)

0

亚历克斯我有同样的问题,因为你,我打破了他的头一点,写了下面的代码谁回答我问题,充满希望我也会见你或他人。

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;