【问题标题】:Error reading/writing to properties of generic type using RTTI使用 RTTI 读取/写入泛型类型的属性时出错
【发布时间】:2020-08-05 09:29:17
【问题描述】:

我正在使用泛型类来允许我访问泛型类型的命名属性并读/写它的值。尝试从 RTTIProperty 记录访问 GetValue 调用的结果以及使用 SetValue 设置值时,我收到 EAccessViolation 错误。运行跟踪时,访问 TValue 时似乎都抛出了两个错误。我在下面包含了一个示例控制台应用程序,它突出了这个问题。

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> }

function TAccessData<T>.GetTValue(AItem: T; AField: string): 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(@AItem);
end;

var
  LTestObj : TTestClass;
  LAccessOBj : TAccessData<TTestClass>;
  AValue : TValue;

procedure TAccessData<T>.SetTValue(AItem: T; Afield: string; 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(@AItem, AValue);
end;

begin
  try
    LTestObj := TTestClass.Create;
    LTestObj.Item := 'Hello';
    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');
    Writeln(AValue.ToString); // <--- This results in a EAccessViolation
    LAccessOBj.SetTValue(LTestObj,'Item','World'); // <--- This results in a EAccessViolation
    Writeln(LTestObj.Item);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

我怀疑我在访问泛型类型的属性的方式上遗漏了一些东西,但是对于我为什么会出现这种行为已经碰壁了。我没有对泛型参数做出限制,因为我也需要它来处理记录类型。

使用东京更新 1

【问题讨论】:

    标签: delphi generics rtti


    【解决方案1】:

    您在GetTValueSetTValue 中的代码存在缺陷,因为它将@AItem 传递给TRttiProperty.SetValueGetValue。它需要是PPointer(@AItem)^ 或将T 限制为class,这样您就可以直接使用Pointer(AItem) 进行硬铸。

    由于错误传递的AInstance TValue 包含一些垃圾内存,如果您引入字符串变量并在尝试将其传递给Writeln 之前将ToString 调用的结果分配给它,您可以看到这些内存.然后Writeln 中的代码正在生成 AV。

    【讨论】:

    • 谢谢@Stefan,我认为这可能是我做错了一些简单的事情
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 2015-12-12
    • 2015-02-15
    • 2020-01-29
    • 2012-12-17
    • 2017-05-29
    • 2017-12-31
    相关资源
    最近更新 更多