我想知道如何执行通用TObjectList<T>
容器的序列化。基本上,我想在该列表中存储不同的对象,但所有对象都将从TSerializable
下降,其定义如下:
TSerializable = class abstract(TObject)
public
{ Public declarations }
procedure LoadFromStream(const S: TStream); virtual; abstract;
procedure SaveToStream(const S: TStream); virtual; abstract;
end;
现在,假设我在我的应用程序中的某处定义了这些类:
type
TExampleClass = class(TSerializable)
private
{ Private declarations }
FIntProp: Integer;
public
{ Public declarations }
constructor Create();
procedure LoadFromStream(const S: TStream); override;
procedure SaveToStream(const S: TStream); override;
property IntProp: Integer read FIntProp write FIntProp;
end;
TAnotherExample = class(TSerializable)
private
{ Private declarations }
FStringProp: String;
public
{ Public declarations }
constructor Create();
procedure LoadFromStream(const S: TStream); override;
procedure SaveToStream(const S: TStream); override;
procedure ReverseStringProp();
property StringProp: String read FStringProp write FStringProp;
end;
我打算将这些对象存储在列表中:
var
MS: TMemoryStream;
SomeList: TObjectList<TSerializable>;
begin
MS := TMemoryStream.Create();
SomeList := TObjectList<TSerializable>.Create(True);
try
SomeList.Add(TExampleClass.Create());
SomeList.Add(TAnotherClass.Create());
TExampleClass(SomeList[0]).IntProp := 1992;
TAnotherClass(SomeList[1]).StringProp := 'Some value';
// Here, a method to serialize the list...
SerializeList(SomeList, MS);
// Clear the list and reset position in the stream.
SomeList.Clear();
MS.Seek(0, soFromBeginning);
// Unserialize the list.
UnserializeList(SomeList, MS);
// Should display "Some value".
Writeln(TAnotherClass(SomeList[1]).StringProp);
finally
SomeList.Free();
MS.Free();
end;
end;
现在,我怎么可能将整个列表序列化为流,然后从该流重新创建列表?
我在想的是:
SaveToStream()
。但是对于这种工作方法,我需要创建一种类寄存器,它可以是某种存储已知类的字典。这听起来是个好主意,但后来我需要调用一些RegisterClass()
方法将每个新类添加到字典中,我不太喜欢这样。
还有其他方式,还是我应该像我提议的那样做?
非常感谢。
答案 0 :(得分:1)
谢谢大家的提示。我决定使用自己的方法,这可能不是最好的方法,但适合我的小项目的需要。
我认为有人可能会对这种方法感兴趣,所以我在这里发布了它。
基本上,我决定使用基类TSerializable
:
type
TSerializable = class abstract(TObject)
public
{ Public declarations }
procedure LoadFromStream(const S: TStream); virtual; abstract;
procedure SaveToStream(const S: TStream); virtual; abstract;
end;
每个后代类都需要实现LoadFromStream()
和SaveToStream()
并处理保存以单独流式传输。编写一些通用方法可能会很好,这些方法会自动加载/保存所有类属性。
然后,我有这个小班:
type
TSerializableList = class(TObjectList<TSerializable>)
public
procedure Serialize(const S: TStream);
procedure UnSerialize(const S: TStream);
end;
代码是:
{ TSerializableList }
procedure TSerializableList.Serialize(const S: TStream);
var
CurrentObj: TSerializable;
StrLen, StrSize: Integer;
ClsName: String;
begin
S.Write(Self.Count, SizeOf(Integer));
for CurrentObj in Self do
begin
ClsName := CurrentObj.QualifiedClassName();
StrLen := Length(ClsName);
StrSize := SizeOf(Char) * StrLen;
S.Write(StrLen, SizeOf(Integer));
S.Write(StrSize, SizeOf(Integer));
S.Write(ClsName[1], StrSize);
CurrentObj.SaveToStream(S);
end;
end;
procedure TSerializableList.UnSerialize(const S: TStream);
var
I, NewIdx, TotalCount, Tmp, Tmp2: Integer;
ClsName: String;
Context: TRttiContext;
RttiType: TRttiInstanceType;
begin
Context := TRttiContext.Create();
try
S.Read(TotalCount, SizeOf(Integer));
for I := 0 to TotalCount -1 do
begin
S.Read(Tmp, SizeOf(Integer));
S.Read(Tmp2, SizeOf(Integer));
SetLength(ClsName, Tmp);
S.Read(ClsName[1], Tmp2);
RttiType := (Context.FindType(ClsName) as TRttiInstanceType);
if (RttiType <> nil) then
begin
NewIdx := Self.Add(TSerializable(RttiType.MetaclassType.Create()));
Self[NewIdx].LoadFromStream(S);
end;
end;
finally
Context.Free();
end;
end;
快速而肮脏,但适用于我需要的东西。
注意强>
由于代码使用扩展的RTTI,因此无法在较旧的Delphi版本中进行编译。此外,您可能需要在DPR文件中添加{$STRONGLINKTYPES ON}
或者发明一些其他机制,以便链接器不会跳过您的类(David Heffernan建议单向here)