Delphi 通过RTTI在字段中记录值

Delphi 通过RTTI在字段中记录值,delphi,Delphi,我们需要使用带有预设值的rtti创建一个类。这些值取自属性。当您需要在该领域增加价值时,一切似乎都很好。找到正确的属性并获取属性的值为true。但这一记录并未被操作。告诉我哪里错了 program DemoGenerator; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Rtti; Type // My attribute DemoDataAttribute = class(TCustomAttribu

我们需要使用带有预设值的rtti创建一个类。这些值取自属性。当您需要在该领域增加价值时,一切似乎都很好。找到正确的属性并获取属性的值为true。但这一记录并未被操作。告诉我哪里错了

program DemoGenerator;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Rtti;

Type
  // My attribute
  DemoDataAttribute = class(TCustomAttribute)
  private
    FGenerator: String;
  public
    constructor Create(Generator: String);
  published
    property Generator: string read FGenerator write FGenerator;
  end;

  // 
  TSomeType = Class
  private
    fPhone: string;
  published
    [DemoData('+1800764328')]
    property Phone: string read fPhone write fPhone;
  End;

  // 
  TMegaSuperClass = Class
    Function Go<T: Class, constructor>: T;
  End;

Procedure Test;
var
  LMsc: TMegaSuperClass;
  New: TSomeType;
Begin
  LMsc := TMegaSuperClass.Create;
  try
    New := LMsc.Go<TSomeType>;
    Writeln('New.Phone: ' + New.Phone);
  finally
    LMsc.Free;
    // New.Free;
  end;
End;

{ DemoDataAttribute }

constructor DemoDataAttribute.Create(Generator: String);
begin
  FGenerator := Generator;
end;

{ TMegaSuperClass }

function TMegaSuperClass.Go<T>: T;
var
  LContext: TRttiContext;
  LClass: TRttiInstanceType;
  LProp: TRttiProperty;
  LAttr: TCustomAttribute;
  LField: TRttiField;
begin
  // Init Rtti
  LContext := TRttiContext.Create;
  LClass := LContext.GetType(T) as TRttiInstanceType;
  Writeln('LClass: ' + LClass.ToString);
  // Result
  Result := T.Create;
  for LProp in LClass.GetProperties do
  begin
    Writeln('LProp: ' + LProp.ToString);
    for LAttr in LProp.GetAttributes do
    begin
      Writeln('LAttr: ' + LAttr.ToString);
      if LAttr is DemoDataAttribute then
      Begin
        Writeln('Attr value: ' + DemoDataAttribute(LAttr).Generator);
        // How write value?
        LProp.SetValue(@Result, DemoDataAttribute(LAttr).Generator);
      End;
    end;
  end;
end;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    Test;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.
程序演示生成器;
{$APPTYPE控制台}
{$R*.res}
使用
System.SysUtils,
系统Rtti;
类型
//我的属性
DemoDataAttribute=类(TCustomAttribute)
私有的
FGenerator:字符串;
公众的
构造函数创建(生成器:字符串);
出版
属性生成器:字符串读取FGenerator写入FGenerator;
结束;
// 
TSomeType=Class
私有的
fPhone:字符串;
出版
[解调数据('+1800764328')]
物业电话:字符串读fPhone写fPhone;
结束;
// 
TMegaSuperClass=类
功能Go:T;
结束;
程序测试;
变量
LMsc:tmega超类;
新:TSomeType;
开始
LMsc:=TMegaSuperClass.Create;
尝试
新:=LMsc.Go;
Writeln('New.Phone:'+New.Phone);
最后
LMsc.免费;
//新的。免费的;
结束;
结束;
{DemoDataAttribute}
构造函数dematataAttribute.Create(生成器:字符串);
开始
FGenerator:=发电机;
结束;
{tmega超类}
函数TMegaSuperClass.Go:T;
变量
LContext:trttitcontext;
l类:trttInstanceType;
LProp:trtti属性;
LAttr:TCustomAttribute;
LField:TRttiField;
开始
//初始Rtti
LContext:=TRttiContext.Create;
LClass:=LContext.GetType(T)作为TRttiInstanceType;
Writeln('LClass:'+LClass.ToString);
//结果
结果:=T.Create;
对于LClass.GetProperties中的LProp
开始
Writeln('LProp:'+LProp.ToString);
对于LProp.GetAttributes中的LAttr
开始
Writeln('LAttr:'+LAttr.ToString);
如果LAttr是DemoDataAttribute,则
开始
Writeln('Attr value:'+DemoDataAttribute(LAttr.Generator));
//如何写值?
LProp.SetValue(@Result,DemoDataAttribute(LAttr.Generator));
结束;
结束;
结束;
结束;
开始
尝试
{TODO-oUser-cConsole Main:在此处插入代码}
试验;
Readln;
除了
关于E:Exception-do
Writeln(E.ClassName,“:”,E.Message);
结束;
结束。
控制台输出:

TSomeType
酒店电话:字符串
解调器属性
值:+1800764328
电话:

像这样:

LProp.SetValue(Pointer(Result), DemoDataAttribute(LAttr).Generator);

SetValue
的第一个参数声明为
Instance:Pointer
。类引用只是指向实例的指针,这正是您所需要的。

我认为对
指针的强制转换是必要的,但现在无法检查。强制转换是必需的(否则不会编译)。您也可以这样强制转换:
TObject(Result)
,它也可以工作。问题是,我真的不明白为什么需要这样的强制转换,因为编译器可以利用泛型上的当前约束(类和构造函数约束不应该暗示TObject后代吗?)