将一个TObject对象保存到文件中

11
如何将一个对象以其当前状态保存到文件中,以便可以立即读取并恢复其所有变量?

维基百科说:“特定对象属性的值集合称为其状态。” Delphi实际上没有内置机制来保存完整的对象状态,而只支持已发布的属性。在我看来,RTTI应该扩展为可选择包括所有字段。 - mjn
这种情况有更新吗? - NaN
7个回答

4
你需要的是对象持久化。这篇文章可能会有所帮助,如果你搜索“delphi 持久化对象”,还会有更多相关内容。

1
可能这个人使用了谷歌,谷歌又指向了Stackoverflow。"问谷歌"不是一个真正的答案。相反,Francois的回答是一个真正(且有用)的答案。 - Gabriel
不错(而且显而易见)的解决方案,但实用性有限。楼主要求保存一个对象。你的解决方案对于TObject是行不通的。 - Gabriel

3
如前所述,最简单的方法是使用Stream及其WriteComponentReadComponent方法。
但请注意:
- 它仅适用于TComponent的子类,而不适用于普通的TObject;
- 仅适用于已发布的属性(保存在dfm中的属性),而不适用于公共属性,更不用说私有属性了;
- 当恢复组件时,您必须特别注意Name属性
您可以在以下SO答案中找到一些可用代码:在Delphi中运行时替换可视化组件在运行时复制组件

不错(而且显而易见)的解决方案,但实用性有限。楼主要求保存一个对象。 - Gabriel

3
如果您将对象从TComponent派生,您可以使用一些内置功能将对象流式传输到文件中。我认为这仅适用于简单的对象。
以下是一些示例代码,可帮助您入门:
unit Unit1;

interface

uses
  Classes;

type
  TMyClass = class(TComponent)
  private
    FMyInteger: integer;
    FMyBool: boolean;
    FMyString: string;
  public
    procedure ToFile(AFileName: string);
  published
    property MyInteger: integer read FMyInteger write FMyInteger;
    property MyString: string read FMyString write FMyString;
    property MyBool: boolean read FMyBool write FMyBool;
  end;

implementation

{ TMyClass }

procedure TMyClass.ToFile(AFileName: string);
var
  MyStream: TFileStream;
begin
  MyStream := TFileStream.Create(AFileName);
  try
    Mystream.WriteComponent(Self);
  finally
    MyStream.Free;
  end;
end;

end.

不错(而且显而易见)的解决方案,但实用性有限。楼主要求保存一个对象。 - Gabriel

2

这里在S.O.上还有一种自己编写的XML方法


2
解决方案1:你可以使用JVCL TJvAppXMLFileStorage(见下面的代码)。但是JVCL非常庞大!如果你想要或者不想要在你的整个生活中拖着这样一个巨大的依赖,你必须考虑两次。
解决方案2:将对象保存到二进制文件中(我更喜欢的解决方案)。相信我,这并不难。 在我的LightSaber核心库中使用ccStreamMem.pas,或者更好地使用ccStreamBuff.pas(缓冲写入)。 在Delphi全盛时期一书中有一些代码示例。 附注:LightSaber是JVCL的轻量级替代品。
以下是如何将记录保存到二进制文件的示例。对于TObject,操作是相同的!
// Supposing you have a record (could be also an object) called RAnimationParams that you want to save to disk:

INTERFACE

USES
  System.SysUtils, Vcl.Graphics, ccStreamBuff;

TYPE
  TFrameDelayType = (fdAuto, fdUser);

  RAnimationParams = record
    Color         : TColor;          
    DelayType     : TFrameDelayType;
    procedure WriteToStream (IOStream: TCubicBuffStream);
    procedure ReadFromStream(IOStream: TCubicBuffStream);
  end;

IMPLEMENTATION

procedure RAnimationParams.WriteToStream(IOStream: TCubicBuffStream);
begin
 IOStream.WriteByte    (Ord(DelayType));
 IOStream.WriteInteger (Color);
 IOStream.WritePadding (32);
end;

procedure RAnimationParams.ReadFromStream(IOStream: TCubicBuffStream);
begin
 DelayType    := TFrameDelayType(IOStream.ReadByte);
 Color        := IOStream.ReadInteger;
 IOStream.ReadPadding (32);
end;

在结尾处的填充允许您稍后更改记录/对象结构,而无需更改二进制文件的格式。
要将RAnimationParams实际保存到磁盘上,只需执行以下操作:
MyBinFile:= TCubicBuffStream.CreateRead('C:\Test.bin');
AnimationParams.WriteToStream(MyBinFile);
MyBinFile.Free;

当您想从Test.bin加载RAnimationParams时,使用相同的代码,但是使用CreateWrite而不是CreateRead。

TCubicBuffStream类甚至还有专门的函数,如ReadHeader/CreateWrite,可让您轻松地向二进制文件中添加“file magic numbers”和“file version numbers”。

看到了吗?并不难。而且它适用于任何对象,不仅限于TComponent。


这是JVCL的示例,但它不适用于TObject,只适用于持续派生类:

 uses
      JvAppXMLStorage;
    
    var
      Storage: TJvAppXMLFileStorage;
    begin
      Storage := TJvAppXMLFileStorage.Create(nil);
      try
        Storage.WritePersistent('', MyObject);
        Storage.Xml.SaveToFile('S:\TestFiles\Test.xml');
        Storage.Xml.LoadFromFile('S:\TestFiles\Test.xml');
        Storage.ReadPersistent('', MyObject);
      finally
        Storage.Free;
      end;
    end;

E2010 Incompatible types: 'TPersistent' and 'TSomething.TElse<TYetAnother>' - user30478

1

这里有一个很好的教程。请记住,使用这种方法在运行时保存对象需要具备RTTI(运行时类型信息),因此它只能捕获类的发布属性。


-1
你已经得到了一些关于你问题的好答案。根据你实际所做的事情,使用预先构建的库或组件来保存对象可能是可取的。这是一个廉价而巧妙的库/组件集,使持久化和恢复对象变得微不足道,并且很容易(即通过少量代码)适应持久化对象的未发布成员: http://www.deepsoftware.ru/rsllib/index.html 这不是什么高深的技术,但如果你经常做这种事情,这个组件提供了一个不错的框架。
Developer Express还包括一个通用的cxPropertiesStore组件,作为ExpressEditors库的一部分,该库随其某些组件一起提供。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接