Delphi 动态结构的大小

Delphi 动态结构的大小,delphi,struct,sizeof,Delphi,Struct,Sizeof,这将(希望)很快得到解决,这是我的问题: 我有一个结构 PMacro = ^TMacro; TMacro = class Hotkey: Integer; Command: String; CTRLMode: boolean; RepeatInterval: integer; constructor Create(Hotkey: Integer; Command: String; CTRLMode: boolean; RepeatInterval:

这将(希望)很快得到解决,这是我的问题:

我有一个结构

PMacro = ^TMacro;
  TMacro = class
    Hotkey: Integer;
    Command: String;
    CTRLMode: boolean;
    RepeatInterval: integer;

    constructor Create(Hotkey: Integer; Command: String; CTRLMode: boolean; RepeatInterval: integer); overload;
    constructor Create; overload;
    procedure Execute;
  end;
我需要得到它的大小(通过TFileStream保存)。此类的实例存储在其他位置的列表中,这是我的保存例程:

Stream:=TFileStream.Create(FileName,fmCreate or fmOpenWrite);
  for i := 0 to Macros.Count-1 do
  begin
    Macro:=TMacro(Macros[i]);
    Size:=sizeof(Macro);
    Stream.Write(size,SizeOf(integer));
    Stream.Write(Macro,sizeof(Macro));
  end;
SizeOf(宏)返回4字节,这将是指针,但我需要特定实例占用的实际空间。我想到的第一件事是获取
Length(Command)
,因为它是一个返回指针大小的动态结构。但这意味着要有类似于
SizeOf(Integer)+Length(Command)+SizeOf(boolean)+……
的东西,但这不利于TMacro结构的进一步扩展

那个么,有并没有一种方法可以获得包含动态类型的结构的大小呢


感谢您的回答

因为Delphi对象是引用类型,
SizeOf()
返回引用的大小,该大小与指针的大小相同,在当前Delphi版本中为4字节

如果记录中的数据是值类型,则
SizeOf()
将返回内容的大小

但是,由于您的结构包含托管类型,即字符串,因此不能像这样简单地将其保存在一个大glob中。您需要对字符串进行特殊处理

如果我是你,我会逐项保存信息。特别是,这使您能够控制诸如对齐之类的问题,并允许您满足版本控制的需要。您可以很容易地编写自己的基本代码来实现这一点。但是,如果

,您可能需要考虑使用第三方框架。
  • 有很多字段需要保存,单独处理它们将导致非常费力的代码
  • 您希望为文件格式的版本控制提供一些灵活性。例如,当您添加新字段、更改现有字段的含义等时,您可能希望考虑软件的未来版本会发生什么

如果要获取TMacro的大小,请调用InstanceSize方法。但这不会帮助您将其块写入流,也不会更改为包含字符串的大小,因为字符串是引用类型

你不能这样写你的TMacro结构。首先,它是一个类,而不是一个记录,这意味着它包含一个不想保存的“magic”字段(如果您使用的是Delphi 2009或更高版本,则包含其中的两个)。第二,即使它是一个记录,它仍然包含一个引用类型(字符串),因此数据不会内联存储在TMacro中;它存储在堆上,必须单独访问

如果需要实现序列化,可以采用两种不同的方法。创建一对方法,如下所示:

procedure Load(savefile: TStream); //can also be implemented as a constructor
procedure Save(savefile: TStream);

然后将它们实现为一个接一个地读取/写入每个字段,或者将某种类型的通用序列化程序与RTTI一起使用。这在Delphi 2010中编写起来要容易得多,因为它有一组更广泛的RTTI功能可用。

如果您希望SizeOf为您提供一个记录的大小,然后您可以对其进行二进制持久化,那么您可以(尽管我个人不推荐二进制记录持久化)使用一种记录类型

我认为,澄清这一问题不仅需要大卫的答案和石匠,还需要必然的原则:

如果SizeOf(RECORDTYPE)是您想要输出的,首先使用RECORD(非Class),然后使用100%值类型(如Char array(非String))生成二进制持久记录:

  type
    TMyCharType = UnicodeChar; // or AnsiChar. Your choice.  
    PMacro = ^TMacro;
    TMacro = record
      Hotkey: Integer;
      Command: Array [0..1000] of TMyCharType;  
      CTRLMode: Boolean;
      RepeatInterval: integer;
    end;
就风格而言,我倾向于使用基于类的系统,该系统使用比基于记录的二进制存储更高级的持久化风格。但若你们想这样做,那个么就用RECORD,就像我说的,不要用Class,也不要用String

此外,请注意,在您的代码示例中,如果TMacro是一个类,那么PMacro=^TMacro实际上比错误更糟糕。(除非你真的想做某种双指针间接操作。)


TMacro(引用类型)已经是通过引用的,因此不需要获取它们的地址,因为它们在TMacro类型的变量(如果是类)之间作为指针进行内部传递。因此,您真的需要弄清楚代码中的记录类型和值类型。

您能创建一个将实例保存在TMacro中的过程吗?这是什么意思?如果你的意思是我创建了一个过程,它逐个遍历所有元素并保存它们,那么是的,我可以(但我认为有更好的方法)。但由于字符串类型的原因,我似乎无法通过一次调用保存整个宏实例。我的意思基本上是@Mason Wheeler所说的。请解释下一票,下一票。David:是否有框架使用RTTI来确定数据类型,然后正确处理流式传输到磁盘?“那不可能吗?”罗伯特,我肯定有很多,但我不知道。我总是自己写。谢谢。我声明了PMacro,只是因为我陷入了我的一种方法(后来证明是错误的),所以它是多余的,我忘了删除它。不管怎样,我试着用数组[1..1000]的字符;但delphi不允许我将其作为字符串使用(我相信字符串只是一个开放的字符数组?),并对我大喊“不兼容类型”。那么基本上可以声明一个大于256个字符的固定字符串吗?(并像处理字符串一样处理它,即STR:='abcd')