Инспектор на обекти Delphi RTTI

Опитвам се да създам опростен инспектор на обекти за приложение за рисуване, което пиша.

Опитвам се динамично да получа RTTI за избрания обект и неговите дъщерни обекти. Ако дадено свойство е клас (tkClass), искам да извикам GetRTTIObject рекурсивно, предавайки това свойство като обект, за да получа подсвойствата за него (т.е. BaseObj.Brush.Color или BaseObj.Pen.Width и т.н.). Подозирам, че искам да предам екземпляра на този обект и че ще бъде болезнено очевидно, когато някой посочи какво е това. Как да накарам екземпляр да премине към моята функция? Или трябва да търся TRttiInstance за свойства, които са класове....?

Знам, че работи на ниво 0, защото мога да предам BaseObject.Brush към първото си извикване на GetRTTIObject и получавам списък със свойства на TBrush. Как мога да разбия рекурсивно?

Изглежда получавам някакъв указател със стойност := GetPropValue(AObj, Prop.Name);

Дереферирам ли това по някакъв начин, за да получа своя екземпляр...?

Поздрави, Роб

Опростеният тестов клас е дефиниран:

TBaseClass = class(TObject)
  private
    FFont: TFont;
    FBrush: TBrush;
    FPen: TPen;
    FCaption: String;
    FFloat1: Real;
    FInt1: Integer;
  published
    property Font: TFont Read FFont Write FFont;
    property Brush: TBrush Read FBrush Write FBrush;
    property Pen: TPen Read FPen Write FPen;
    property Caption: String Read FCaption Write FCaption;
    property Float1: Real Read FFloat1 Write FFloat1;
    property Int1: Integer Read FInt1 Write FInt1;
end;

Моята RTTI процедура е:

procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
  LContext: TRttiContext;
  LType: TRttiType;
  Prop: TRttiProperty;
  PropString: String;
  PropInfo: PPropInfo;
  Tabs: String;
  I: Integer;
  Value: Variant;
begin
  LContext := TRttiContext.Create();

  try
    for I := 0 to Indent do
      Tabs := Tabs + '  '; //chr(9)

    Log(Format('Get RTTI (Class) for "%s"', [AClass.ClassName]));

    LType := LContext.GetType(AClass.ClassInfo);

    Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
    Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);

    Items.Add(Tabs + '-- Properties --');

    for Prop in LType.GetProperties do
    begin
      PropString := 'property: ' + Prop.Name;

      PropInfo := GetPropInfo(AClass, Prop.Name);
      PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind));

      if propInfo <> nil then begin
        PropString := PropString + ': ' + PropInfo^.PropType^.Name;

        case propInfo.PropType^.Kind of
          tkClass: begin
           PropString := PropString + ' (Class)' ; // ' GetProp Value: ' + IntToHex(PropInfo.GetProc, 8); //     Items.Add('--- Get RTTI ---');(Class)';
           Log(Format('GetRTTI: %s (%s)', [Prop.Name, PropInfo^.PropType^.Name]));
           // TODO: Get a reference to the object and call GetRTTI
           // TODO: Or change function to work from classtype rather than object

//           GetRTTIObject(### WHAT GOES HERE?!?!?, Items, Indent + 1);// := PropString + ' Class';
          end;

        end;
      end;

      Items.Add(Tabs + PropString);

    end;
  finally
    LContext.Free;
  end;
end;

Опа!!

Виждам, че съм поставил грешна функция .....въпросната взема TObject и присвояването е:

LType := LContext.GetType((AObject.ClassInfo); (AObject.ClassType също изглежда работи...)....

Не в моята станция за разработка точно сега, но мисля, че всичко останало е същото след това....


person Robatron    schedule 28.12.2020    source източник


Отговори (2)


Проблем във вашия пример, че TBrash има свойството TBitMap, TBitMap има TCanvas, TCanvas има TBrash. Извикването на функцията GetRTTIClass ще бъде безкрайно рекурсивно. Но ако направите условие за получаване на RTTI само веднъж за всеки клас, е възможно да поправите вашата функция.

uses System.Generics.Collections;
var ListClasses:TList<TClass>;
    LContext : TRttiContext;
implementation

procedure TfrmMain.FormCreate(Sender: TObject);
begin
 LContext := TRttiContext.Create();
 ListClasses:=TList<TClass>.Create;
end;

procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
  LType: TRttiType;
  Prop: TRttiProperty;
  PropString: String;
  Tabs: String;
  I: Integer;
begin
  if ListPrinted.Contains(AClass) then Exit
                                  else ListPrinted.Add(AClass);
  for I := 0 to Indent do Tabs := Tabs + '  ';
  LType := LContext.GetType(AClass.ClassInfo);
  Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
  Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
  Items.Add(Tabs + '-- Properties --');
  for Prop in LType.GetProperties do  begin
    PropString := 'property: ' + Prop.Name;
    PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind))+' '+Prop.PropertyType.Name;
    Items.Add(Tabs + PropString);
    case Prop.PropertyType.Handle^.Kind of
      tkClass: begin
        GetRTTIClass(Prop.PropertyType.Handle^.TypeData^.ClassType, Items,Indent+2);
      end;
    end;
end;
procedure TfrmMain.btn1Click(Sender: TObject);
begin
  GetRTTIClass(TBaseClass, Items,0);
end;
person Andriy Sokolov    schedule 28.12.2020
comment
Благодаря за отговора!!! Някой въпроси: - person Robatron; 30.12.2020
comment
2) Защо да създавате RTTIContext във FormCreate, а не в процедурата GetRTTI....? - person Robatron; 30.12.2020
comment
3) Къде мога да намеря повече за нотацията ListClasses:TList‹TClass› и TList‹TClass›.Create....? - person Robatron; 30.12.2020

Добре, направих някои промени в процедурата. Разборът на класа не е напълно достатъчен. Имам нужда от манипулатори на инстанции.

За рекурсивно извикване на моята процедура (тази, която приема обект, а не клас като първи параметър), имам нужда от екземпляра на подобекта (AObj.Font, например). Мога да го придобия с:

case Prop.PropertyType.TypeKind of
  tkClass: begin
    SubObj := GetObjectProp(AObj, Prop.Name);
    GetRTTIObject2(SubObj, Tree, ChildNode, Indent + 2);
  end;
end;

Толкова просто, наистина, след като го обгърнах.

Все пак ще гласувам за другия отговор като решение, тъй като даде добри насоки за избягване на друг капан. :)

person Robatron    schedule 30.12.2020