Delphi 2010中的Rtti数据操作和一致性

3
有没有人有想法,如何使用对原始数据的引用来使 TValue 生效?在我的序列化项目中,我使用了一个通用的序列化器(如 XML-Serialization 中所建议的),它将 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属性,但是由于我在DataModel中使用了很多Rtti,所以我不想随时这样做。

也许有人有更好的建议?

2个回答

4
一个TValue旨在以非常紧凑的形式保存任何类型的数据,它不是设计成模拟“指针”。看看RTTI.pas单元:TValue是一个仅有一个数据成员TValueData类型的记录。TValueData本身是一个变体记录。
查看TValueData,您会发现它只保存最少量的数据,无法知道TValue来自哪里。
解决方案:不要在结构中保存TValue,用TRttiField + TObject代替。当您需要TValue时,请使用TRttiField.GetValue(Instance);当您想设置值时,请使用TRttiField.SetValue(Instance,AValue:TValue)。

这是第一种方法,您是正确的:这将适用于对象。但是如果T是记录而不是TObject呢? - Christian Metzler
1
如果这是一个记录,我假设你会有一个普通的指针。TRttiField.GetValue和TRttiField.SetValue需要一个指针类型的实例参数,而不是TObject……只是我从未使用过记录的RTTI,也从未有过这样的需求。Delphi 2010中的RTTI旨在很好地处理记录和类!让我换一种方式表达:用RTTI获取的任何对象(TRttiProperty?)代替TRttiField,在TRtti对象所需的任何东西的位置上使用任何对象代替TObject。你已经拥有了一切所需,因为你首先找到了获取TValue的方法。 - Cosmin Prund

0

感谢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或使用原始记录类来更改字段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接