Обработка и согласованность данных Rtti в Delphi 2010

Есть ли у кого-нибудь идеи, как я могу сделать TValue, используя ссылку на исходные данные? В моем проекте сериализации я использую (как предлагается в XML-Serialization) универсальный сериализатор, который хранит TValue во внутренней древовидной структуре (аналогично 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, я действительно не хочу делать это в любое время.

Может быть, у кого-то есть предложение получше?


person Christian Metzler    schedule 05.05.2010    source источник


Ответы (2)


TValue предназначен для хранения любых данных в очень компактной форме, он не предназначен для имитации «указателя». Взгляните на модуль RTTI.pas: TValue - это ЗАПИСЬ, которая имеет только один член данных типа TValueData. TValueData сама по себе является вариантной записью.

Посмотрев на TValueData, вы увидите, что он НЕ содержит ничего, кроме минимального количества данных: нет способа узнать, откуда взялось это TValue.

Решение: не храните TValue в своих структурах, замените его парой TRttiField + TObject. Когда вам нужно TValue, используйте TRttiField.GetValue (Instance), когда вы хотите установить значение, используйте TRttiField.SetValue (Instance, AValue: TValue).

person Cosmin Prund    schedule 05.05.2010
comment
Это был первый подход, и вы правы: он будет работать для объектов. Но что, если T - это запись, а не TObject? - person Christian Metzler; 05.05.2010
comment
Если это запись, я предполагаю, что у вас будет простой указатель. TRttiField.GetValue и TRttiField.SetValue принимают параметр Instance типа Pointer, а не TObject ... просто я никогда не использовал RTTI для записей, и в этом никогда не было необходимости. RTTI в Delphi 2010 предназначен для удобной обработки как записей, так и классов! Позвольте мне сформулировать это иначе: вместо TRttiField используйте в первую очередь любой объект, который вы получили от RTTI (TRttiProperty?), А вместо TObject используйте все, что требуется вашему объекту TRtti. У вас уже есть все, что вам нужно, поскольку вы изначально нашли способ получить TValue. - person Cosmin Prund; 05.05.2010

Спасибо 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 через или используя исходный класс записи.

person Christian Metzler    schedule 05.05.2010