SizeOf一个动态结构

时间:2011-05-06 16:15:56

标签: delphi struct sizeof

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

我有一个结构

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(Macro)返回4bytes,这将是指针,但我需要特定实例所需的实际空间。我想到的第一件事是获得Length(Command),因为它是一个返回其指针大小的动态结构。但这意味着有类似SizeOf(Integer)+Length(Command)+SizeOf(boolean)+...的东西,但这对于进一步扩展TMacro结构是不利的。

那么,有没有办法获得包含动态类型的结构的大小?

感谢您的回答

3 个答案:

答案 0 :(得分:4)

如果要获得TMacro的大小,请调用InstanceSize方法。但这不会帮助您将其阻塞到流中,并且它不会更改为包含字符串的大小,因为该字符串是引用类型。

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

如果需要实现序列化,可以通过几种不同的方式实现。要么像这样创建一对方法:

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

然后将它们逐个读取/写入每个字段,或者使用RTTI的某种通用序列化程序。这在Delphi 2010中更容易编写,因为它具有更广泛的RTTI功能集。

答案 1 :(得分:2)

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

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

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

如果我是你,我会逐项保存信息。特别是,这使您可以控制对齐等问题,并允许您满足版本控制的需要。您可以轻松编写自己的基本代码来执行此操作。但是,您可能需要考虑使用第三方框架:

  • 要保留很多字段并单独处理它们会导致非常繁琐的代码。
  • 您希望为文件格式的版本控制构建一些灵活性。例如,您可能希望考虑在添加新字段时更改软件的未来版本会发生什么,更改现有字段的含义等。

答案 2 :(得分:0)

如果你想让SizeOf给你一个你可以二进制持久存储的记录的大小,你可以(尽管我个人不建议使用二进制记录持久性)使用RECORD类型。

我认为,澄清这个问题不仅需要大卫的回答,而且需要梅森,还需要一个合理的原则:

如果你想要弄清楚SizeOf(RECORDTYPE),首先使用RECORD(不是Class),第二次使用像Char数组(不是String)这样的100%值类型会产生二进制可持续记录:

  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类型的变量(如果它是一个类)的内部传递为指针。因此,您真的需要清楚地了解代码中的记录类型和值类型。