Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
delphi中的对象复制_Delphi_Deep Copy - Fatal编程技术网

delphi中的对象复制

delphi中的对象复制,delphi,deep-copy,Delphi,Deep Copy,我有一个复杂的对象需要深度复制(很多数组、对象、指针、继承层的层、数百个不同类型的成员等等),通过Delphi的Assign方法重新创建它是没有效率的,很可能太复杂了 我一直在研究Rtti,这似乎是一个不错的选择,但到目前为止,我无法涵盖所有可能的场景。我不想浪费这么多时间,希望找到一个好的简单的例子。不幸的是,我还没有找到。到目前为止,我一直在使用循环(TRttiType.GetFields())遍历对象中的所有TRttiField),并尝试使用基于TTypeKind值的指针分配所有内容。(t

我有一个复杂的对象需要深度复制(很多数组、对象、指针、继承层的层、数百个不同类型的成员等等),通过Delphi的Assign方法重新创建它是没有效率的,很可能太复杂了

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

我发现了一个JSON/编组示例,但它无法深度复制我的复杂对象;我犯了错误

内部:当前不支持类型tkPointer


Delphi中是否有类似于C#二进制序列化和使用内存流创建深度副本的东西。或者您知道Delphi中有一个简单的好例子,使用RTTI或JSON/编组进行深度复制,可以处理最复杂的对象吗?

简单地说,您不能使用RTTI来简化深度复制。(这将比使用经典的赋值覆盖更复杂,更容易出错)


因此,您需要更仔细地查看持续的及其子对象,并正确地重写赋值,赋值方法(没有更简单的方法)

Alex我和您有同样的问题,我稍微打断了他的头,并编写了以下代码来回答我的问题,希望也能遇到你或其他人

function TModel.Clone(pObj:TObject): TObject;
procedure WriteInField(pField:TRttiField; result, source:Pointer);
var
  Field:TRttiField;
  Val:TValue;
  Len: NativeInt;
  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;
函数TModel.Clone(pObj:TObject):TObject;
过程写入字段(pField:TRttiField;结果,源:指针);
变量
字段:TRttiField;
Val:TValue;
伦:民族主义;
I:整数;
tp:trtti型;
ctx:trtti上下文;
开始
如果不是pField.GetValue(source).IsEmpty,则
case pField.FieldType.TypeKind of
TTypeKind.tkRecord:
开始
对于pField.FieldType.GetFields中的字段
WriteInField(字段,PByte(结果)+pField.Offset,pField.GetValue(源).GetReferenceToRawData);
结束;
TTypeKind.tkClass:
开始
Val:=Self.Clone(pField.GetValue(source.AsObject));
如果赋值为(TObject(pField.GetValue(result.AsObject)),则
pField.GetValue(result).AsObject.Free;
pField.SetValue(结果,Val);
结束;
TTypeKind.tkDynArray:
开始
Len:=pField.GetValue(源).GetArrayLength;
对于I:=0到Len-1 do
案例pField.GetValue(源代码).GetArrayElement(I)
TTypeKind.tkRecord:
开始
tp:=ctx.GetType(pField.GetValue(源).GetArrayElement(I).TypeInfo);
对于tp.GetFields中的字段
WriteInField(Field,PByte(结果)+Field.Offset,pField.GetValue(源).GetReferenceToRawData);
结束;
TTypeKind.tkClass:
开始
Val:=Self.Clone(pField.GetValue(源).GetArrayElement(I).AsObject);
DynArraySetLength(PPointer(PByte(结果)+pField.Offset)^,pField.GetValue(源).TypeInfo,1,@Len);
pField.GetValue(结果).SetArrayElement(I,Val);
结束;
其他的
DynArraySetLength(PPointer(PByte(结果)+pField.Offset)^,pField.GetValue(源).TypeInfo,1,@Len);
pField.GetValue(结果).SetArrayElement(I,pField.GetValue(源).GetArrayElement(I));
结束;
结束;
其他的
pField.SetValue(结果,pField.GetValue(源));
结束;
结束;
变量
语境:语境;
IsComponent,lookouthornameprop:Boolean;
rttType:trtType;
方法:trtti法;
MinVisibility:TMemberVisibility;
参数:焦油;
PropFild:TRttiField;
Fild:TRTTIFELD;
SourceAsPointer,ResultAsPointer:指针;
ObjWithData:TObject;
价值:TValue;
开始
尝试
如果分配(pObj),则
ObjWithData:=pObj
其他的
ObjWithData:=自身;
RttiType:=Context.GetType(ObjWithData.ClassType);
//找到一个合适的构造函数,尽管要特别对待组件
IsComponent:=(ObjWithData是TComponent);
对于RttiType.GetMethods中的方法
如果Method.IsConstructor,则
开始
Params:=Method.GetParameters;
如果Params=nil,则中断;
if(长度(参数)=1)和IsComponent和
(Params[0]。ParamType是TRttiInstanceType)和
SameText(Method.Name,'Create'),然后Break;
结束;
如果Params=nil,则
结果:=Method.Invoke(ObjWithData.ClassType,[]).AsObject
其他的
引发异常。CreateFmt('对象对克隆无效:“”%s“”,[ObjWithData.ClassName]);
尝试
//循环通过道具,复制读/写道具的值
移动(ObjWithData、SourceAsPointer、SizeOf(指针));
移动(结果、结果指针、大小指针);
对于RttiType.GetFields中的PropFild
WriteInField(PropFild、ResultAsPointer、SourceAsPointer);
除了
结果:免费;
提高;
结束;
最后
ObjWithData:=零;
结束;
结束;

Jerry,这个类已经继承了TPersistent,赋值被重写。除非有自动方式,否则我必须手动将数百个对象分配给彼此。(我尝试调用继承的Assign,但它抛出了一个类似“无法将MyObject分配给MyObject”的错误。尽管在调用Assign之前我检查了正确的对象类型,但仍发生了这种情况。)数百个成员?听起来你需要给这个坏男孩瘦身。值得一提的是,这里有数百个关于持久性的问题。已经有很多答案了。不,
Assign
不是这样工作的。您应该覆盖
AssignTo
,并为copying@userAssignTo是您在没有cont时覆盖的内容