Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Ошибка компиляции Delphi 12.3: "Type parameter 'T' must be a non-nullable value type" в общих шаблонах и управляемых записях

Delphi , Синтаксис , Ошибки и Исключения

 

В Delphi 12.3 Athens появилась ошибка компиляции, связанная с использованием общих шаблонов (generics) и управляемых записей (managed records). Ошибка проявляется как "[dcc32 Error] Type parameter 'T' must be a non-nullable value type", и возникает, когда общий шаблон с ограничением : record используется с записью, содержащей управляемые типы данных, такие как string, динамические массивы или пользовательские атрибуты.

Суть проблемы:

Ранее, в версиях Delphi до 12.3, компилятор допускал использование управляемых записей в общих шаблонах с ограничением : record. Однако, в Delphi 12.3 это поведение было изменено, и теперь компилятор выдает ошибку. Официальная позиция Embarcadero заключается в том, что это изменение "as designed" (задумано так) и связано с предотвращением ошибок, которые могли возникать из-за особенностей управления памятью управляемых записей.

Пример кода, вызывающего ошибку:

unit UTestGenericsBug;

interface

uses
  System.Rtti,
  Data.DB;

type
  DBField = class(TCustomAttribute)
  strict private
    FFieldType: TFieldType;
    FLength: Integer;
  public
    constructor Create(const AFieldType: TFieldType = ftUnknown; const ALength: Integer = 0);
    property FieldType: TFieldType read FFieldType;
    property Length: Integer read FLength;
  end;

  TTestRecord = record
  private
    [DBField(ftString, 20)]
    FTerminalName: string;
  public
  end;

  TRecordLoader<T: record> = class sealed
    class function Get: T; static;
  end;

implementation

constructor DBField.Create(const AFieldType: TFieldType = ftUnknown; const ALength: Integer = 0);
begin

end;

class function TRecordLoader<T>.Get: T;
begin
  Result := Default(T);
end;

function Get: TTestRecord;
begin
  // Ошибка компиляции здесь
  Result := TRecordLoader<TTestRecord>.Get;
end;

end.

В данном примере, запись TTestRecord содержит поле FTerminalName типа string и атрибут DBField. При попытке использовать TTestRecord в качестве параметра типа T для общего класса TRecordLoader<T: record>, компилятор выдает ошибку.

Решение (Workaround):

Предложенное решение заключается в явном указании полного объявления общего типа при использовании класса TRecordLoader. Вместо:

Result := TRecordLoader<TTestRecord>.Get;

Следует использовать:

Result := TRecordLoader<TTestRecord>.Get<TTestRecord>;

Это позволяет компилятору корректно определить тип параметра и избежать ошибки. Однако, как отмечают пользователи, такое решение нивелирует преимущество использования ограничения : record, поскольку теперь общий шаблон может принимать типы, для которых он не был предназначен.

Альтернативные решения и последствия:

  1. Использование записей без управляемых типов: Можно перепроектировать записи таким образом, чтобы они не содержали управляемые типы данных, такие как string, динамические массивы и т.д. Вместо string можно использовать ShortString (например, string[255]), но это может привести к проблемам совместимости и необходимости конвертации данных. Как было отмечено в обсуждении, это также может привести к предупреждениям компилятора W1057 "Implicit string cast from 'ShortString' to 'string'".

     TTestRecord = record
    private
    [DBField(ftString, 20)] FTerminalName: string[20]; // Использование ShortString
    public
    end;

  2. Недостатком этого подхода является ограничение максимальной длины строки и потенциальные проблемы с кодировкой (ShortString использует кодировку ANSI).

  3. Использование классов вместо записей: Можно заменить запись классом. Однако, это может потребовать значительной переработки кода и повлиять на производительность, поскольку работа с классами требует выделения памяти в куче и управления ею.

  4. Остаться на Delphi 12.2 или более ранней версии: Если изменение поведения компилятора в Delphi 12.3 критично для вашего проекта, можно рассмотреть возможность остаться на предыдущей версии Delphi. Однако, это означает, что вы не сможете воспользоваться новыми функциями и исправлениями ошибок, доступными в Delphi 12.3.

  5. Использовать TValue: Можно использовать TValue для передачи значений, что позволяет обойти ограничение на типы данных. Однако, это может потребовать дополнительной работы по упаковке и распаковке значений.

Вывод:

Изменение поведения компилятора в Delphi 12.3, касающееся использования управляемых записей в общих шаблонах с ограничением : record, представляет собой серьезную проблему для разработчиков, использующих эти возможности. Предложенное решение с явным указанием типа в общих шаблонах является лишь обходным путем, который не решает проблему в полной мере. Альтернативные решения требуют значительной переработки кода и могут повлиять на производительность и совместимость. В данной ситуации, разработчикам следует тщательно оценить все возможные варианты и выбрать наиболее подходящий для их конкретного проекта. Также, стоит обратиться в Embarcadero с запросом о пересмотре данного изменения в будущих версиях Delphi.

Создано по материалам из источника по ссылке.

В Delphi 12.3 Athens возникла ошибка компиляции при использовании управляемых записей в общих шаблонах с ограничением `: record`, что требует обходных решений или переработки кода.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Ошибки и Исключения ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-05-01 09:53:26/0.0040249824523926/0