Пара проблем с вашим кодом.
Вы не инициализируете переменную Clone равной nil. Что на моей машине привело к нарушениям доступа в верхнем методе CloneInstance, поскольку клон не был создан, поскольку переданное значение было не ноль.
У вас нет параметра DestinationInstance, объявленного как var. Это означает, что создание экземпляра в верхнем методе CloneInstance не возвращается к вызывающей стороне. Добавление var
к параметру решает проблему. Вам нужно использовать TObject(Clone)
при вызове CloneInstance из основного метода программы, иначе Delphi будет жаловаться на то, что «нет перегруженного метода, который можно вызывать с этими параметрами». Это потому, что параметры var хотят, чтобы их точный объявленный тип передавался в них.
Я изменил ваш код на:
uses
SysUtils,
TypInfo,
rtti;
type
TPerson = class(TObject)
public
Name: string;
constructor Create;
destructor Destroy(); Override;
end;
constructor TPerson.Create;
begin
WriteLn('A TPerson was created');
end;
destructor TPerson.Destroy;
begin
WriteLn('A TPerson was freed.');
inherited;
end;
procedure CloneInstance(SourceInstance: TObject; var DestinationInstance: TObject; Context: TRttiContext); Overload;
var
rSourceType: TRttiType;
rDestinationType: TRttiType;
rField: TRttiField;
rSourceValue: TValue;
Destination: TObject;
rMethod: TRttiMethod;
begin
rSourceType := Context.GetType(SourceInstance.ClassInfo);
if (DestinationInstance = nil) then begin
rMethod := rSourceType.GetMethod('Create');
DestinationInstance := rMethod.Invoke(rSourceType.AsInstance.MetaclassType, []).AsObject;
end;
for rField in rSourceType.GetFields do begin
if (rField.FieldType.TypeKind = tkClass) then begin
// TODO: Recursive clone
end else begin
// Non-class values are copied (NOTE: will cause problems with records etc.)
rField.SetValue(DestinationInstance, rField.GetValue(SourceInstance));
end;
end;
end;
procedure CloneInstance(SourceInstance: TObject; var DestinationInstance: TObject); Overload;
var
rContext: TRttiContext;
begin
rContext := TRttiContext.Create();
CloneInstance(SourceInstance, DestinationInstance, rContext);
rContext.Free();
end;
var
Original: TPerson;
Clone: TPerson;
begin
Clone := nil;
ReportMemoryLeaksOnShutdown := true;
Original := TPerson.Create();
Original.Name := 'Marjan';
CloneInstance(Original, TObject(Clone));
Original.Name := 'Original';
WriteLn('Original name: ', Original.Name);
WriteLn('Clone name: ', Clone.Name);
Clone.Free();
Original.Free();
ReadLn;
end.
Я добавил конструктор, чтобы увидеть, как создаются экземпляры, а также пару строк для проверки имен после клонирования. Вывод гласит:
A TPerson was created
A TPerson was created
Original name: Original
Clone name: Marjan
A TPerson was freed.
A TPerson was freed.