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

Решение проблемы доступа к именованным свойствам обобщенного типа в Delphi и Pascal

Delphi , Компоненты и Классы , RTTI

При работе с обобщенными классами в Delphi для чтения и записи значений именованных свойств обобщенного типа часто используют механизмы RTTI. Однако, в некоторых случаях при попытке доступа к свойствам обобщенного типа через RTTI возникают ошибки доступа (EAccessViolation). Рассмотрим пример такой ситуации и возможные способы решения проблемы.

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

В представленном ниже коде показана попытка доступа к свойству Item обобщенного класса TTestClass через обобщенный класс TAccessData<T>. При выполнении метода GetTValue и SetTValue возникает исключение EAccessViolation.

program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  System.RTTI;
Type
  TTestClass = class
  private
    FItem: string;
  public
    Property Item: string read FItem write FItem;
  end;
  TAccessData<T> = class
  Function GetTValue(AItem : T; AField : string) : TValue;
  Procedure SetTValue(AItem : T; Afield : string; AValue : TValue);
  end;
  // Остальная часть реализации TAccessData<T>
  // ...
begin
  try
    LTestObj := TTestClass.Create;
    LTestObj.Item := 'Hello';
    // Вывод значения свойства Item
    Writeln(LTestObj.Item);
    LAccessObj := TAccessData<TTestClass>.Create;
    AValue := LAccessObj.GetTValue(LTestObj, 'Item');
    // Вывод типа значения
    Writeln(AValue.TypeInfo^.Name);
    if AValue.TypeInfo.Kind <> tkString then
      Writeln('Not string');
    // Вывод значения свойства, что приводит к EAccessViolation
    Writeln(AValue.ToString);
    // Установка значения свойства, что также приводит к EAccessViolation
    LAccessObj.SetTValue(LTestObj, 'Item', 'World');
    // Вывод обновленного значения свойства
    Writeln(LTestObj.Item);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Описание проблемы

Ошибка возникает из-за неправильной передачи указателя на объект в методы TRttiProperty.GetValue и TRttiProperty.SetValue. Для корректной работы с объектами классов необходимо использовать PPointer(@AItem)^ или ограничить обобщенный параметр T типом class, чтобы можно было использовать Pointer(AItem).

Подтвержденный ответ

Для исправления ошибки необходимо изменить передачу параметра AItem в методы GetTValue и SetTValue обобщенного класса TAccessData<T>. Вместо @AItem следует использовать PPointer(@AItem)^ или применить ограничение обобщенного параметра T классом, что позволит использовать Pointer(AItem).

Исправленный код

program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  System.RTTI;
Type
  TTestClass = class
  private
    FItem: string;
  public
    Property Item: string read FItem write FItem;
  end;
  TAccessData<T> = class
  Function GetTValue(AItem : T) const PPointer : TValue; overload;
  Procedure SetTValue(AItem : T) const PPointer; const AField : string; const AValue : TValue; overload;
  end;
  // Остальная часть реализации TAccessData<T>
  // Изменения в функциях GetTValue и SetTValue
  // ...
  function TAccessData<T>.GetTValue(AInstance : T) const PPointer : TValue;
  var
    LContext : TRttiContext;
    LType : TRttiType;
    LProperty : TRttiProperty;
  begin
    Result := nil;
    LType := LContext.GetType(TypeInfo(T));
    LProperty := LType.GetProperty(AField);
    if LProperty <> nil then
      Result := LProperty.GetValue(Pointer(AInstance));
  end;
  // ...
  procedure TAccessData<T>.SetTValue(AInstance : T) const PPointer; const AField : string; const AValue : TValue;
  var
    LContext : TRttiContext;
    LType : TRttiType;
    LProperty : TRttiProperty;
  begin
    LType := LContext.GetType(TypeInfo(T));
    LProperty := LType.GetProperty(AField);
    if LProperty <> nil then
      LProperty.SetValue(Pointer(AInstance), AValue);
  end;
  // ...

Альтернативный ответ (ограничение обобщенного параметра)

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

TAccessData<T: class, TValueOfT> = class
  // ...
end;

Это позволит использовать методы GetTValue и SetTValue, применяя кастинг к Pointer(AItem).

Заключение

При работе с RTTI важно правильно передавать указатели на объекты в соответствующие методы. Исправление кода, представленное выше, позволит избежать ошибок доступа к свойствам обобщенного типа в Delphi.

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

Вопрос связан с решением проблемы доступа к именованным свойствам обобщенных типов в языке программирования Delphi, используя механизмы RTTI, и возможными способами устранения ошибки `EAccessViolation` при этом доступе.


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

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




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


:: Главная :: RTTI ::


реклама


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

Время компиляции файла: 2024-12-22 20:14:06
2025-02-11 00:11:57/0.0017490386962891/0