【问题标题】:SizeOf a dynamic structure动态结构的大小
【发布时间】:2011-05-07 03:23:01
【问题描述】:

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

我有一个结构

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

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

感谢您的回答

【问题讨论】:

  • 您能创建一个将实例保存在 TMacro 中的过程吗?
  • 你这是什么意思?如果您的意思是我创建一个过程,它会逐个遍历所有元素并保存它们,那么是的,我可以(但我认为有更好的方法)。但由于 string 类型,我似乎无法通过一次调用保存整个 Macro 实例。
  • 我的意思是@Mason Wheeler 所说的。
  • 解释你的反对票,反对票。

标签: delphi struct sizeof


【解决方案1】:

如果要获取 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 功能集。

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • David:是否有框架使用 RTTI 来确定数据类型,然后正确处理流式传输到磁盘?还是不可能?
    • @robert 我敢肯定有很多,但我不知道。我一直都是自己写的。
    【解决方案3】:

    如果您希望 SizeOf 为您提供可以二进制持久化的记录大小,您可以(尽管我个人不推荐二进制记录持久性)使用 RECORD 类型。

    我认为,要弄清楚这个问题,不仅需要大卫和梅​​森的回答,还需要推论:

    如果 SizeOf(RECORDTYPE) 是您想要弄清楚的,首先使用 RECORD(不是 Class),然后使用 100% 的值类型,例如 Char 数组(不是 String),结果是 Binary Persistable 记录:

      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 确实比错误更糟糕。 (除非你真的打算做某种双指针间接。)

    TMMacro(引用类型)已经是按引用的,所以不需要获取它们的地址,因为它们在 TMacro(如果它是一个类)类型的变量之间作为指针在内部传递。所以你真的需要弄清代码中的记录类型和值类型。

    【讨论】:

    • 谢谢。我宣布 PMMacro 只是因为我陷入了我的一种方法(后来证明是错误的),所以它是多余的,我忘了删除它。无论如何,我尝试使用 Char 的 Array [1..1000];但是delphi不允许我将它作为字符串使用(我相信String只是一个开放的字符数组?)并对我大喊“不兼容的类型”。那么基本上可以声明一个> 256个字符的固定字符串吗? (并像字符串一样使用它,即 STR:='abcd')
    猜你喜欢
    • 2014-11-11
    • 2010-10-28
    • 1970-01-01
    • 2019-06-27
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    相关资源
    最近更新 更多