Delphi 使用RTTI读取/写入泛型类型的属性时出错

Delphi 使用RTTI读取/写入泛型类型的属性时出错,delphi,generics,rtti,Delphi,Generics,Rtti,我使用泛型类来访问泛型类型的命名属性并读取/写入其值。尝试从RTTIProperty记录访问调用GetValue的结果时,以及使用SetValue设置值时,我遇到了EAccessViolation错误。运行跟踪时,在访问TValue时,似乎会抛出两个错误。我在下面提供了一个示例控制台应用程序,它突出了这个问题 program Project1; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.RTTI; Typ

我使用泛型类来访问泛型类型的命名属性并读取/写入其值。尝试从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;
{$APPTYPE控制台}
{$R*.res}
使用
System.SysUtils,
System.RTTI;
类型
TTestClass=类
私有的
菲特姆:弦;
公众的
属性项:字符串读FItem写FItem;
结束;
TAccessData=class
函数GetTValue(AItem:T;AField:string):TValue;
过程设置值(AItem:T;Afield:string;AValue:TValue);
结束;
{TAccessData}
函数TAccessData.GetTValue(AItem:T;AField:string):TValue;
变量
LContext:trttitcontext;
l型:trtti型;
LProperty:trtti属性;
开始
结果:=无;
LType:=LContext.GetType(Typeinfo(T));
LProperty:=LType.GetProperty(Afield);
如果LProperty为零,则
结果:=LProperty.GetValue(@AItem);
结束;
变量
LTestObj:TTestClass;
LAccessOBj:TAccessData;
AValue:TValue;
过程TAccessData.SetTValue(AItem:T;Afield:string;AValue:TValue);
变量
LContext:trttitcontext;
l型:trtti型;
LProperty:trtti属性;
开始
LType:=LContext.GetType(Typeinfo(T));
LProperty:=LType.GetProperty(Afield);
如果LProperty为零,则
LProperty.SetValue(@AItem,AValue);
结束;
开始
尝试
LTestObj:=TTestClass.Create;
LTestObj.Item:=“你好”;
书面(LTestObj.项目);
LAccessOBj:=TAccessData.Create;
AValue:=LAccessObj.GetTValue(LTestObj,'Item');
Writeln(AValue.TypeInfo^.Name);
如果AValue.TypeInfo.Kind tkString
Writeln('非字符串');

Writeln(AValue.ToString);//
GetTValue
SetTValue
中的代码存在缺陷,因为它将
@AItem
传递到
trttProperty.SetValue
GetValue
。它需要是
p指针(@AItem)^
或将
T
约束到
class
,这样您就可以直接使用
指针(AItem)
进行硬广播


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

谢谢@Stefan,我想可能是我做错了什么