Rtti манипулирование данными и согласованность в Delphi 2010
У кого-нибудь есть идея, как сделать TValue, используя ссылку на исходные данные? В моем проекте сериализации я использую (как предложено в XML-сериализации) универсальный сериализатор, который хранит TValues во внутренней древовидной структуре (аналогично MemberMap в примере).
Это дерево элементов также следует использовать для создания формы динамической настройки и манипулирования данными. Моя идея состояла в том, чтобы определить свойство для данных:
TDataModel <T> = class
{...}
private
FData : TValue;
function GetData : T;
procedure SetData (Value : T);
public
property Data : T read GetData write SetData;
end;
Реализация методов GetData, SetData:
procedure TDataModel <T>.SetData (Value : T);
begin
FData := TValue.From <T> (Value);
end;
procedure TDataModel <T>.GetData : T;
begin
Result := FData.AsType <T>;
end;
К сожалению, метод TValue.From всегда создает копию исходных данных. Таким образом, всякий раз, когда приложение вносит изменения в данные, DataModel не обновляется, и, наоборот, если я изменяю свою DataModel в динамической форме, на исходные данные это не влияет. Конечно, я всегда мог использовать свойство Data до и после изменения чего-либо, но так как я использую много Rtti внутри моей DataModel, я действительно не хочу делать это в любое время.
Возможно, у кого-то есть лучшее предложение?
2 ответа
TValue предназначен для хранения любых данных в очень компактной форме, он не предназначен для эмуляции "указателя". Взгляните на модуль RTTI.pas: TValue - это ЗАПИСЬ, в которой есть только один элемент данных типа TValueData. TValueData сама по себе является вариантом записи.
Взглянув на TValueData, вы увидите, что он НЕ содержит ничего, кроме минимального объема данных: нет способа узнать, откуда пришло это TValue.
Решение: не держите TValue в своих структурах, замените его на пару TRttiField + TObject. Когда вам нужно TValue, используйте TRttiField.GetValue(Instance), когда вы хотите установить значение, используйте TRttiField.SetValue(Instance, AValue:TValue).
Спасибо Cosmin за вашу помощь, решение состоит не в том, чтобы сохранить TValue в структуре, а в использовании указателя на данные и использовании методов GetValue, SetValue поля или свойства.
Итак, вот как я решил это в своем родовом классе:
TDataModel <T> = class
private
FType : PTypeInfo;
FInstance : Pointer;
public
constructor Create (var Data : T);
procedure ShowContent;
end;
constructor TDataModel <T>.Create (var Data : T);
begin
FType := TypeInfo(T);
if FType.Kind = tkClass then
FInstance := TObject (Data)
else if FType.Kind = tkRecord then
FInstance := @Data;
end;
procedure TDataModel <T>.ShowContent;
var
Ctx : TRttiContext;
Field : TRttiField;
begin
for Field in Ctx.GetType (FType).GetFields do
Writeln (Field.GetValue (FInstance).ToString);
end;
Теперь вы можете изменить поля, используя DataModel или исходный класс записи.