Копирование объектов в delphi

у меня есть сложный объект для глубокой копии (множество массивов, объектов, указателей, слоев наследования, сотни членов различных типов и многое другое), и его повторное создание с помощью метода назначения Delphi не является продуктивным и, скорее всего, слишком сложным.

Я смотрел на Rtti и это кажется хорошим вариантом, но до сих пор я не мог охватить все возможные сценарии. Я не хочу тратить столько времени и надеяться найти хороший и простой пример. К Сожалению, Я пока не нашел. То, что я делал до сих пор, - это пройти через все TRttiField в объекте с петлей (TRttiType.GetFields()) и попробуйте назначить все, используя указатели, основанные на TTypeKind значения. (tkPointer, tkClass, tkClassRef...)

Я нашел пример JSON/Marshalling, но он не мог глубоко скопировать мой сложный объект; я получил ошибку;

внутренний: тип tkPointer в настоящее время не поддерживается

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

есть ли что-нибудь в Delphi, близкое к двоичной сериализации C# и созданию глубокой копии с использованием потока памяти. Или есть хороший и простой пример, который вы знаете в Delphi, делая глубокую копию с RTTI или JSON / Marshalling, который будет работать с самыми сложными объектами?

2 ответов


в нескольких словах вы нельзя использовать rtti для упрощения глубокой копии (это будет более сложным и подверженным ошибкам, чем использование классического переопределения)

поэтому вам нужно посмотреть ближе к TPersistent и его дочерние объекты и правильно переопределить назначение, AssignTo методы (нет более простого способа)


Алекс у меня была такая же проблема, как и у вас, я немного сломал голову и написал следующий код, который ответил на мою проблему, надеюсь, также встретится с вами или другими.

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;