3

链接到原始问题是否可以获得类属性的索引?并由 Remy Lebeau 和 RRUZ 回答

program Demo;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, Winapi.Windows,
  System.Rtti, System.TypInfo;

type
  TMyClass = class
  private
    function GetInteger(const Index: Integer): Integer;
    procedure SetInteger(const Index, Value: Integer);
  public
    property P1: Integer Index 1 read GetInteger write SetInteger;
    property P2: Integer Index 2 read GetInteger write SetInteger;
    property P3: Integer Index 3 read GetInteger write SetInteger;
  end;

{ TMyClass }

function TMyClass.GetInteger(const Index: Integer): Integer;
begin
  Result := Index;
end;

procedure TMyClass.SetInteger(const Index, Value: Integer);
begin
  //
end;

{------------------------------------------------------------------------------}
function GetPropertyIndex(const AClass: TClass; APropertyName: string): Integer;
var
  Ctx: TRttiContext;
begin
  Ctx := TRttiContext.Create;
  Result := (Ctx.GetType(AClass).GetProperty(APropertyName) as TRttiInstanceProperty).Index;
end;

{------------------------------------------------------------------------------}
var
  C: Cardinal;
  I: Integer;
  N: Integer;
begin
  try
    C := GetTickCount;
    for N := 1 to 1000000 do
      I := GetPropertyIndex(TMyClass, 'P2');
    WriteLn(GetTickCount - C, ' ms - The index of the property P2 is ', I);

    ReadLn;
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;
end.

在我的电脑上,这个测试大约需要 5 秒。但是当我TRttiContext在调用之前使用GetPropertyIndex()- 例如

...
begin
  try
    TRttiContext.Create.Free;

    C := GetTickCount;
    for N := 1 to 1000000 do
      I := GetPropertyIndex(TMyClass, 'P2');
...

相同的测试只需要 ~ 1 秒。为什么 ?

编辑我在测试第二个示例时发现的问题

...
var
  Ctx: TRttiContext;
  C: Cardinal;
  I: Integer;
  N: Integer;
begin
  try
    C := GetTickCount;
    for N := 1 to 1000000 do
    begin
      Ctx := TRttiContext.Create;
      I := (Ctx.GetType(TMyClass).GetProperty('P2') as TRttiInstanceProperty).Index;
    end;  
    WriteLn(GetTickCount - C, ' ms - The index of the property P2 is ', I);

    ReadLn;
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;
end.

但原因在第二次测试中更明显,问题也是如此。

4

1 回答 1

10

RTTI 数据使用引用计数池进行缓存。您的代码加速如此之快的原因是该TRttiContext.Create.Free语句创建了一个TRttiContext在您的应用程序的生命周期内保持在范围内的临时,维护对该池的活动引用。在其中创建的后续TRttiContext实例GetPropertyIndex()正在重新使用现有池,而不是每次都浪费时间重新创建新池。当 DPR 代码到达end.语句时,tempTRttiContext超出范围并释放其对池的引用。

当您删除该TRttiContext.Create.Free语句时,临时TRttiContext消失了,并且为每个使用的新池创建和销毁TRttiContextGetPropertyIndex()浪费时间一遍又一遍地重新创建相同的缓存。

TRttiContext通过在项目选项中启用调试 DCU,然后在调试器中单步执行代码,您可以轻松地看到这一点。

于 2013-11-01T08:47:24.030 回答