Delphi 中的Serialiation
熟悉MFC的人对Serialize这个虚拟函数大概不会陌生。在MFC中,如果要用Serialization机制读取和保存数据,大概是这样:
void CMyDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << myString;
ar << myInt;
}
else
{
ar >> myString;
ar >> myInt;
}
}
Object Pascal中有没有类似的机制呢?接触到TReader和TWriter这两个类之后,我知道我找到答案了。
不需多讲理论,来看一个实际的例子。在Form上面放三个Edit和两个Button,另外再加一个OpenDialog和SaveDialog。代码如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
if SaveDialog1.Execute then
Serialize(SaveDialog1.FileName,True);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Serialize(OpenDialog1.FileName,False);
end;
procedure TForm1.Serialize(const FileName:string; bSave: Boolean);
var
Reader : TReader;
Writer : TWriter;
Stream : TFileStream;
begin
if bSave then begin
Stream := TFileStream.Create(FileName,fmOpenWrite or fmCreate);
Writer := TWriter.Create(Stream,4096);
Writer.WriteString(Edit1.Text);
Writer.WriteInteger(StrToInt(Edit2.Text));
Writer.WriteFloat(StrToFloat(Edit3.Text));
Writer.Free;
Stream.Free;
end
else begin
Stream := TFileStream.Create(FileName,fmOpenRead);
Reader := TReader.Create(Stream,4096);
Edit1.Text := Reader.ReadString;
Edit2.Text := IntToStr(Reader.ReadInteger);
Edit3.Text := FloatToStr(Reader.ReadFloat);
Reader.Free;
Stream.Free;
end;
end;
如果愿意的话,完全可以将Serialize包装成一个virtual method,从而让派生类中的实现更加简洁。
TReader和TWriter不仅能够读取和写入Object Pascal中绝大部分标准数据类型,而且能够读写Collection/List/Variant这些高级类型,甚至能够读写Perperties和Component。不过,TReader/TWriter自身实际上提供的功能很有限,大部分实际的工作是由TStream这个非常强大的类来完成的。从TReader和TWriter的声明中可以看到一些特别为Component而设计的方法,不难猜想,Delphi开发环境本身很可能就是利用TReader/TWriter,将构件的属性写入.DFM文件以及从.DFM文件中读取属性值的。
下面的例子很有用也非常有趣,它的效果完全相当于Form Designer中的View As Text命令:
var
DFMBuf, TextBuf : TStream;
begin
DFMBuf := TMemoryStream.Create;
DFMBuf.WriteComponent(Self);
TextBuf := TMemoryStream.Create;
DFMBuf.Seek(0,soFromBeginning);
ObjectBinaryToText(DFMBuf,TextBuf);
TextBuf.Seek(0,soFromBeginning);
Memo1.Lines.LoadFromStream(TextBuf);
TextBuf.Free;
DFMBuf.Free;
甚至可以从可执行文件中读取Form的信息:
var
DFMBuf, TextBuf : TStream;
buf : pointer;
begin
DFMBuf := TResourceStream.Create(HInstance,'TForm1',RT_RCDATA);
DFMBuf.Position := 0;
TextBuf := TMemoryStream.Create;
DFMBuf.Seek(0,soFromBeginning);
ObjectBinaryToText(DFMBuf,TextBuf);
TextBuf.Seek(0,soFromBeginning);
Memo1.Lines.LoadFromStream(TextBuf);
TextBuf.Free;
DFMBuf.Free;
end;
(说明:如果窗体不是TForm1,那么请将TResourceStream.Create一句中的第二个参数改为相应的窗体类名。)
好好读懂这些代码,相信自己做一个DFM Viewer也不是遥不可及的事情了吧!