使用 RTTI 的泛型属性错误 reading/writing
Error reading/writing to properties of generic type using RTTI
我正在使用泛型 class 以允许我访问泛型类型的命名 属性 及其值 read/write。在尝试从 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
您在 GetTValue
和 SetTValue
中的代码存在缺陷,因为它将 @AItem
传递给 TRttiProperty.SetValue
和 GetValue
。它需要 PPointer(@AItem)^
或将 T
限制为 class
以便您可以直接使用 Pointer(AItem)
.
进行强制转换
由于错误传递 AInstance
TValue
包含一些垃圾内存,如果您引入字符串变量并将之前 ToString
调用的结果分配给它,您可以看到试图将其传递给 Writeln
。然后 Writeln
中的代码正在生成 AV。
我正在使用泛型 class 以允许我访问泛型类型的命名 属性 及其值 read/write。在尝试从 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
您在 GetTValue
和 SetTValue
中的代码存在缺陷,因为它将 @AItem
传递给 TRttiProperty.SetValue
和 GetValue
。它需要 PPointer(@AItem)^
或将 T
限制为 class
以便您可以直接使用 Pointer(AItem)
.
由于错误传递 AInstance
TValue
包含一些垃圾内存,如果您引入字符串变量并将之前 ToString
调用的结果分配给它,您可以看到试图将其传递给 Writeln
。然后 Writeln
中的代码正在生成 AV。