Размер динамической структуры - PullRequest
3 голосов
/ 06 мая 2011

Это (надеюсь) будет решено довольно быстро, это моя проблема:

У меня есть структура

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.

Итак, есть ли способ получить размер структуры, содержащей динамический тип?

Спасибо за ответы

Ответы [ 3 ]

4 голосов
/ 06 мая 2011

Если вы хотите получить размер 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 голосов
/ 06 мая 2011

Поскольку объект Delphi является ссылочным типом, SizeOf() возвращает размер ссылки, который равен размеру указателя, 4 байта в текущих версиях Delphi.

Если у вас есть данные в записи, которая является типом значения, то SizeOf() вернет размер содержимого.

Однако, поскольку ваша структура содержит управляемые типы, то есть строку, вы не можете просто сохранить ее в одном большом глобусе, как этот. Вам нужна специальная обработка строки.

На вашем месте я бы сохранил информацию по пунктам. В частности, это дает вам контроль над такими вопросами, как выравнивание и позволяет обслуживать управление версиями. Вы можете легко написать свой очень простой код, чтобы сделать это. Однако вы можете рассмотреть возможность использования сторонних фреймворков, если:

  • Есть много полей для сохранения, и их индивидуальная обработка может привести к очень трудоемкому коду.
  • Вы хотите создать некоторую гибкость для управления версиями формата файла. Например, вы можете подумать о том, что произойдет в будущей версии программного обеспечения, когда вы добавите новое поле, измените значение существующего поля и т. Д.
0 голосов
/ 06 мая 2011

Если вы хотите, чтобы 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.

Кроме того, обратите внимание, что в вашем примере кода PMacro = ^ TMacro действительно хуже- неправильно, если TMacro - это класс.(Если вы действительно не хотите делать какую-то косвенную косвенную указатель.)

TMacro (ссылочные типы) уже являются ссылочными, поэтому нет необходимости брать их адреса, потому что они передаются внутри междупеременные типа TMacro (если это класс) в качестве указателя.Так что вам действительно нужно получить некоторую ясность в отношении типов записей и типов значений в вашем коде.

...