2013-04-30 1 views
8

나는 복잡한 객체 (많은 배열, 객체, 포인터, 상속 계층, 다양한 유형의 멤버 등)를 가지고 있으며 Delphi의 Assign 메소드를 통해 다시 작성합니다. 생산적이지 않고 가장 복잡한 것 같습니다.델파이에서 객체 복사

나는 Rtti을보고 있었지만 좋은 선택 인 것처럼 보이지만 지금까지는 모든 가능한 시나리오를 다룰 수 없었습니다. 나는 많은 시간을 낭비하고 좋고 간단한 예를 찾고 싶지 않다. 불행히도 아직 찾을 수 없습니다. 지금까지 내가 수행 한 작업은 루프 (TRttiType.GetFields())가있는 객체의 TRttiField을 모두 살펴보고 TTypeKind 값을 기반으로하는 포인터를 사용하여 모든 것을 할당하려고했습니다. (tkPointer, tkClass, tkClassRef ...)

JSON/Marshalling 예제를 찾았지만 복잡한 개체를 완전히 복사 할 수 없습니다. 나는 오류가있다.

내부 : 유형 tkPointer 현재

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

델파이 C# 바이너리 직렬화에 근접하고 메모리 스트림을 사용하여 깊은 사본을 만드는 있나요

를 지원하지 않습니다. 또는 Delphi에서 RTTI 또는 JSON/Marshalling을 사용하여 가장 복잡한 객체로 작업 할 수있는 완전한 복사를 수행한다는 것을 알 수 있습니다. 몇 마디에

+0

Jerry,이 클래스는 이미 TPersistent를 상속하며 할당이 무시됩니다. 이 작업을 수행하는 자동 방법이 없으면 수동으로 수백 개의 개체를 서로 할당해야합니다. (나는 상속 된 Assign을 호출 해 보았습니다. "MyObject를 MyObject에 할당 할 수 없습니다"와 같은 오류가 발생했습니다.이 문제는 Assign을 호출하기 전에 올바른 객체 유형을 확인하고 있었지만 발생했습니다.) – Alex

+3

수백 명의 멤버가 필요합니까? 이 나쁜 소년을 좀 더 날씬하게 만들어야 할 것 같은데. 여기에 수백 가지의 끈질긴 질문이 있습니다. 이미 많은 답변이 있습니다. –

+2

아니요, '할당'이이 방식으로 작동하지 않습니다. ** 당신은 ** AssignTo를 오버라이드하고 복사를위한 수단을 제공해야합니다. – OnTheFly

답변

5

당신은 깊은 복사 그래서 당신은 TPersistent의 가까이 볼 필요가

(이 고전 할당 재정의를 사용하는 것보다 쉬운 방법이 더 복잡하고 오류가있을 것이다) 단순화하기 위해 RTTI를 사용할 수 없습니다과 그 자식 개체 제대로

0

알렉스 난 당신과 같은 문제가 있었다 (더 간단한 방법이 없습니다) 지정, AssignTo 메소드를 오버라이드 (override), 조금 머리를 깨고 대답 다음 코드를 쓴 내 문제, 희망 너도 다른 사람을 만난다.

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;