是否可以序列化未封装在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中的情况下进行流式传输吗?
答案 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实现)在设计时进行序列化。