Linked to the original question Is it possible to get the index of class property? and answered by Remy Lebeau and 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.

On my PC this test takes ~ 5 sec. But when I use TRttiContext before calling GetPropertyIndex() - for example

...
begin
  try
    TRttiContext.Create.Free;

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

same test takes only ~ 1 sec. Why ?

Edit The problem I found when I tested secon example as

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

But the reason is more obvious in the second test as is the question.

有帮助吗?

解决方案

RTTI data is cached using a reference counted pool. The reason your code speeds up so much is because the TRttiContext.Create.Free statement creates a temp TRttiContext that stays in scope for the lifetime of your app, maintaining an active reference to that pool. Subsequent TRttiContext instances created inside of GetPropertyIndex() are re-using the existing pool and not wasting time re-creating new pools each time. When the DPR code reaches the end. statement, the temp TRttiContext goes out of scope and releases its reference to the pool.

When you remove the TRttiContext.Create.Free statement, the temp TRttiContext disappears, and a new pool is created and destroyed for each TRttiContext that GetPropertyIndex() uses, wasting time re-creating the same cache over and over.

You can easily see this in action by enabling Debug DCUs in the Project Options and then stepping into the TRttiContext code in the debugger.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top