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