我有以下处理
TStream
写入和读取的代码,但我的问题(问题)是使用 TILObjects
类读取/写入 TListHelper
。请阅读这些方法中的注释:
procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
type
IBuffer = interface
procedure WriteInteger(Value: Integer);
function ReadInteger:Integer
end;
TBuffer = class(TInterfacedObject, IBuffer)
private
FStream: TStream;
public
constructor Create(AStream: TStream);
procedure WriteInteger(Value: Integer);
function ReadInteger: Integer;
end;
type
ILObject = interface
['{D4E3A925-58F2-4E8C-93E7-B49C1C3B453D}']
procedure Serialize(Stream: IBuffer);
function Deserialize(Stream: IBuffer): ILObject;
end;
TILObject = class(TInterfacedObject, ILObject)
public
procedure Serialize(Stream: IBuffer); virtual; abstract;
function Deserialize(Stream: IBuffer): ILObject; virtual; abstract;
end;
type
IListHelper<T> = interface
procedure Serialize(const Buffer: IBuffer; const List: IList<T>);
function Deserialize(const Buffer: IBuffer): IList<T>;
end;
TListHelper<T> = class(TInterfacedObject, IListHelper<T>)
public
procedure Serialize(const Buffer: IBuffer; const List: IList<T>);
function Deserialize(const Buffer: IBuffer): IList<T>;
end;
// test classes
TChild = class(TILObject)
private
public
constructor Create; overload; override;
destructor Destroy; override;
procedure Serialize(Stream: IBuffer); override;
class function Deserialize(const Stream: IBuffer): TChild; reintroduce; overload;
end;
TFather = class(TILObject)
private
FChildList: IList<TChild>;
procedure ReadChild(const Stream: IBuffer);
public
constructor Create; overload; override;
destructor Destroy; override;
procedure Serialize(Stream: IBuffer); override;
class function Deserialize(const Stream: IBuffer): ILObject; reintroduce; overload;
property ChildList: IList<TChild> read FChildList write FChildList;
end;
//
implementation
constructor TBuffer.Create(AStream: TStream);
begin
inherited Create;
FStream := AStream;
end;
procedure TBuffer.WriteInteger(Value: Integer);
begin
FStream.WriteBuffer(Value, SizeOf(Value));
end;
function TBuffer.ReadInteger: Integer;
begin
FStream.ReadBuffer(Result, SizeOf(Result));
end;
procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
Count, i: Integer;
begin
Count := List.Count;
Buffer.WriteInteger(Count);
for i := 0 to Count - 1 do
begin
if TypeInfo(T) = TypeInfo(Integer) then
Buffer.WriteInteger(Integer(TValue.From<T>(List[I]).AsType<Integer>))
// here how deal with the TILObjects.Serialize
// I want to have TILObject.Serialize(Buffer)
// else if TILObject
else
raise Exception.Create('Unsupported type for List serialization');
end;
end;
function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
Count, i: Integer;
Item: TValue;
LObject: TILObject;
begin
Result := TList<T>.Create;
Count := Buffer.ReadInt32;
for i := 0 to Count - 1 do
begin
if TypeInfo(T) = TypeInfo(Integer) then
Item := TValue.From<Integer>(Buffer.ReadInteger)
// here how to deal with TILObject.Deserialize
// else if TILObject.Deserialize(Buffer)
else
raise Exception.Create('Unsupported type for List deserialization');
Result.Add(Item.AsType<T>);
end;
end;
// test classes
constructor TChild.Create;
begin
inherited Create;
end;
destructor TChild.Destroy;
begin
inherited Destroy;
end;
procedure TChild.Serialize(Stream: IBuffer);
begin
inherited Serialize(Stream);
end;
constructor TFather.Create;
begin
inherited Create;
FChildList := TList<TChild>.Create;
end;
class function TChild.Deserialize(const Stream: IBuffer): ILObject;
begin
Result := TChild.Create;
end;
如何在
TIlObject.Serialize()
内使用TListHelper<T>.Serialize()
和在TILObject.Deserialize()
内使用TListHelper<T>.Deserialize()
?
你可以尝试这样的事情:
procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
Count, i: Integer;
val: TValue;
LIntf: ILObject;
LObject: TObject;
begin
Count := List.Count;
Buffer.WriteInteger(Count);
for i := 0 to Count - 1 do
begin
val := TValue.From<T>(List[I]);
case GetTypeKind(T) of
tkInteger: begin
Buffer.WriteInteger(val.AsType<Integer>);
end;
tkInterface: begin
if Supports(val.AsInterface, ILObject, LIntf) then
LIntf.Serialize(Buffer)
else
raise Exception.Create('Unsupported interface type for List serialization');
end;
tkClass: begin
LObject := val.AsObject;
if LObject.InheritsFrom(TILObject) then
TILObject(LObject).Serialize(Buffer)
else
raise Exception.Create('Unsupported object type for List serialization');
end;
else
raise Exception.Create('Unsupported type for List serialization');
end;
end;
end;
function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
Count, i: Integer;
Item: TValue;
LIntf: ILObjct;
LObject: TILObject;
begin
Result := TList<T>.Create;
Count := Buffer.ReadInt32;
for i := 0 to Count - 1 do
begin
case GetTypeKind(T) of
tkInteger: begin
Item := TValue.From<T>(Buffer.ReadInteger);
end;
tkInterface: begin
LIntf := ???.Create as ILObject;
LIntf.Deserialize(Buffer);
Item := TValue.From<T>(LIntf);
end;
tkClass: begin
LObject := ???.Create as ILObject;
LObject.Deserialize(Buffer);
Item := TValue.From<T>(LObject);
end;
else
raise Exception.Create('Unsupported type for List deserialization');
end;
Result.Add(Item.AsType<T>);
end;
end;
但是,正如你所看到的,
TListHelper.Deserialize()
有一个问题。您必须首先创建一个对象实例才能在其上调用 (T)ILObject.Deserialize()
,因此您需要知道要创建哪种类类型。您无法调用 T.Create
,因为 T
没有 class
约束。因此,您必须将类名写入 Buffer
,将其读回,然后使用单独的工厂从指定的类名创建并返回 (T)ILObject
实例,例如:
procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
Count, i: Integer;
val: TValue;
LIntf: ILObject;
LObject: TObject;
begin
Count := List.Count;
Buffer.WriteInteger(Count);
for i := 0 to Count - 1 do
begin
val := TValue.From<T>(List[I]);
case GetTypeKind(T) of
tkInteger: begin
Buffer.WriteInteger(val.AsType<Integer>);
end;
tkInterface: begin
if Supports(val.AsInterface, ILObject, LIntf) then
begin
Buffer.WriteString((LIntf as TObject).ClassName);
LIntf.Serialize(Buffer);
end else
raise Exception.Create('Unsupported interface type for List serialization');
end;
tkClass: begin
LObject := val.AsObject;
if LObject.InheritsFrom(TILObject) then
begin
Buffer.WriteString(LObject.ClassName);
TILObject(LObject).Serialize(Buffer)
end else
raise Exception.Create('Unsupported object type for List serialization');
end;
else
raise Exception.Create('Unsupported type for List serialization');
end;
end;
end;
function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
Count, i: Integer;
Item: TValue;
LClassName: string;
LIntf: ILObjct;
LObject: TILObject;
begin
Result := TList<T>.Create;
Count := Buffer.ReadInt32;
for i := 0 to Count - 1 do
begin
case GetTypeKind(T) of
tkInteger: begin
Item := TValue.From<T>(Buffer.ReadInteger);
end;
tkInterface: begin
LClassName := Buffer.ReadString;
LIntf := CreateILObject(LClassName) as ILObject; // <--
LIntf.Deserialize(Buffer);
Item := TValue.From<T>(LIntf);
end;
tkClass: begin
LClassName := Buffer.ReadString;
LObject := CreateILObject(LClassName); // <--
LObject.Deserialize(Buffer);
Item := TValue.From<T>(LObject);
end;
else
raise Exception.Create('Unsupported type for List deserialization');
end;
Result.Add(Item.AsType<T>);
end;
end;
您必须自己实现
CreateILObject()
,例如:
function CreateILObject(AClassName: string): TILObject;
begin
if AClassName = 'TChild' then
Result := TChild.Create
...
else
Result := nil; // or raise
end;
有多种方法可以概括这样的工厂,但这超出了这个问题的范围。 StackOverflow(和其他论坛)上还有许多与(反)序列化对象相关的其他问题。