TCollection的序列化未在TComponent中声明?

时间:2012-01-14 15:01:27

标签: serialization persistence delphi tcollection

是否可以序列化未封装在TComponent中的TCollection?

例如,我有一个自定义的TCollection。我不能在我的TCollection后代上使用TMemoryStream.WriteComponent()。它只有在我将集合封装在TComponent中然后编写这个组件时才有效。

从技术上讲,没有问题,但声明一个只有 拥有TCollection的TComponent似乎有点奇怪。

TMyCustomCollection = Class(TCollection) // not serializable ?
  //...
End;

TMyCustomCollectionCapsule = Class(TComponent) // serializable !
Private
  FMyCusColl: TMyCustomCollection;
  Procedure SetMyCusColl(Const Data: TMyCustomCollection);
Published
  Property CanBeSerialized: TMyCustomCollection Read FMyCusColl Write SetMyCusColl
End;

也许我只是错过了Delphi RTL的一个功能? TPersistent后代可以在不自身封装在TComponent中的情况下进行流式传输吗?

2 个答案:

答案 0 :(得分:1)

只有将集合放在TComponent中才有效,因为TMemoryStream.WriteComponent(名称本身就是一个线索!)将TComponent作为参数:

procedure WriteComponent(Instance: TComponent);

和TCollection正如您已经发现的那样,不是TComponent后代。让一个TComponent后代只是为了保存你的TCollection后代似乎很奇怪,但是如果你想使用Stream的WriteComponent工具来传输它,我没有看到任何其他简单的方法来做它。


如果你想使用“只是”RTL / VCL(即不使用第三方库),你必须编写一个T(内存)流后代并添加一个带{{1}的WritePersistent实现参数。

我没有删除深入研究TStream类,但我的猜测是你应该能够从TComponent支持中借用很多东西。当然是类继承支持。

粗略地看一下,起初看起来很简单Instance: TPersistent只调用WriteComponent实例化WriteDescendent然后调用该编写器的TWriter方法。 TWriter已经包含了编写集合的方法。

但是,如果你“只是”想要传输TPersistent后代,你将不得不在TWriter / TReader中做很多工作,并且它们完全基于TComponent。这不仅仅是写一些后代的简单案例。首先,他们的TWriter / TReader并没有真正设置为派生自。另一个:TStream(后代)直接实例化TWriter和TReader,这些类没有虚拟构造函数。除非你想尝试挂钩,修补VMT以及更多有趣的东西,否则为他们编写后代是徒劳的。

总而言之:流式传输自定义集合的最简单方法仍然是“只是”将其包装在TComponent中并与之“浪费”。

答案 1 :(得分:1)

您可以通过以下定义的另一个 TComponent descendant 序列化未封装在TComponent中的TCollection:

type
  TCollectionSerializer = class(TComponent)
  protected
    FCollectionData: string;
    procedure DefineProperties(Filer: TFiler); override;
  public
    procedure WriteData(Stream: TStream);
    procedure ReadData(Stream: TStream);
    //
    procedure LoadFromCollection(ACollection: TCollection);
    procedure SaveToCollection(ACollection: TCollection);
  end;

DefineProperties WriteData ReadData 实施细节:

procedure TCollectionSerializer.WriteData(Stream: TStream);
var
  StrStream: TStringStream;
begin
  StrStream:=TStringStream.Create;
  try
    StrStream.WriteString(FCollectionData);
    Stream.CopyFrom(StrStream,0);
  finally
    StrStream.Free;
  end;
end;

procedure TCollectionSerializer.ReadData(Stream: TStream);
var
  StrStream: TStringStream;
begin
  StrStream:=TStringStream.Create;
  try
    StrStream.CopyFrom(Stream,0);
    FCollectionData:=StrStream.DataString;
  finally
    StrStream.Free;
  end;
end;

procedure TCollectionSerializer.DefineProperties(Filer: TFiler);
begin
  inherited;
  //
  Filer.DefineBinaryProperty('CollectionData', ReadData, WriteData,True);
end;

LoadFromCollection SaveToCollection 的模板:

procedure TCollectionSerializer.LoadFromCollection(ACollection: TCollection);
var
  CollectionStream: TStream;
begin
  CollectionStream:= TCollectionStream.Create(ACollection);
  try
    ReadData(CollectionStream)
  finally
    CollectionStream.Free;
  end;
end;

procedure TCollectionSerializer.SaveToCollection(ACollection: TCollection);
var
  CollectionStream: TStream;
begin
  CollectionStream:= TCollectionStream.Create(ACollection);
  try
    WriteData(CollectionStream);
  finally
    CollectionStream.Free;
  end;
end;

关于 TCollectionStream

它应该是一个TStream后代,拥有一个富创建者,其中 TCollection 作为参数,其设计行为类似于TFileStream。你必须实现它。 免责声明:我从未测试过,但我可以告诉TFileStream有效(用于流媒体外部文件)。

<强>结论:

该组件的灵感来自于在Delphi XE(RCData)下在DFM内部序列化外部文件的VCL方式。它必须与组件编辑器一起注册(您还必须基于TComponentEditor实现)在设计时进行序列化。