2013-04-30 16 views
8

Derin bir kopyalı karmaşık bir nesnem var (çok sayıda dizi, nesne, işaretçi, miras katmanları katmanları, yüzlerce farklı türde ve daha fazla üye) ve Delphi'nin Assign yöntemi ile yeniden oluşturuluyor. üretken ve büyük olasılıkla çok karmaşık değil.Delphi'de nesne kopyalama

Rtti'a bakıyorum ve iyi bir seçenek gibi görünüyor, ancak şu ana kadar tüm olası senaryoları karşılayamadım. Çok zaman harcamak ve iyi ve basit bir örnek bulmayı ummak istemiyorum. Maalesef henüz bulamadım. Şu ana kadar yaptığım şey, TRttiField nesnesine bir döngü (TRttiType.GetFields()) ile gidip TTypeKind değerlerini temel alan işaretçiler kullanarak her şeyi atamayı denemektir. (tkPointer, tkClass, tkClassRef ...)

JSON/Marshalling örneğini buldum ancak karmaşık nesneyi kopyalayamadım; Hatam var;

Dahili: Tip tkPointer anda

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

Delphi C# ikili seri yakın, bir bellek akışı kullanarak derin bir kopyasını oluşturarak bir şey var mı

desteklenmez. Ya da en karmaşık nesnelerle çalışacak olan RTTI veya JSON/Marshalling ile Delphi'de bildiğiniz iyi ve basit bir örnek var mı? Birkaç kelime

+0

Jerry, bu sınıf zaten TPersistent'i devralır ve Assign geçersiz kılınır. Bunu yapmanın otomatik bir yolu olmadığı sürece, yüzlerce nesneyi elle elle atamak zorunda kalırdım. (Devralınan Assign'ı çağırmayı denedim ve "MyObject'e MyObject öğesine atanamaz" gibi bir hata attı. Bu, Assign'ı çağırmadan önce doğru nesne türünü kontrol etmeme rağmen oluyordu.) – Alex

+3

Yüzlerce üye? Bu kötü çocuğu biraz indirmen gerekiyormuş gibi geliyor. Buna değdiği için burada SO üzerinde yüzlerce kalıcı soru var. Orada çok fazla cevap var. –

+2

Hayır, "Assign" bu şekilde çalışmıyor. ** ** AssignTo'yu geçersiz kılmanız ve – OnTheFly

cevap

5

Eğer derin kopya Yani TPersistent yakın bakmak gerekir

(klasik atama geçersiz kılma kullanmaktan daha eğilimli bir yol daha karmaşık ve hata olacaktır) basitleştirmek için RTTI kullanamazsınız ve alt nesneleri ve düzgün

0

Alex seninle aynı sorunu vardı (hayır basit bir yolu yoktur) Ata, AssignTo yöntemlerini geçersiz, biraz kafasını kırdı ve cevap aşağıdaki kodu yazdım benim sorun, umutlu ly da senin ya da başkalarıyla tanış.

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;