views:

120

answers:

2

Using the Typinfo unit, it is easy to enumerate properties as seen in the following snippet:

procedure TYRPropertiesMap.InitFrom(AClass: TClass; InheritLevel: Integer = 0);
var
  propInfo: PPropInfo;
  propCount: Integer;
  propList: PPropList;
  propType: PPTypeInfo;
  pm: TYRPropertyMap;
  classInfo: TClassInfo;
  ix: Integer;

begin
  ClearMap;

  propCount := GetPropList(PTypeInfo(AClass.ClassInfo), propList);
  for ix := 0 to propCount - 1 do
  begin
    propInfo := propList^[ix];
    propType := propInfo^.PropType;

    if propType^.Kind = tkMethod then
      Continue; // Skip methods
    { Need to get GetPropInheritenceIndex to work
    if GetPropInheritenceIndex(propInfo) > InheritLevel then
      Continue; // Dont include properties deeper than InheritLevel
    }
    pm := TYRPropertyMap.Create(propInfo.Name);
    FList.Add(pm);
  end;
end;

However, what I need is to figure out the exact class from which each property inherits. For example in TControl, the Tag property comes from TComponent, which gives it an inheritance depth of 1 (0 being a property declared in TControl itself, such as Cursor).

Calculating the inheritance depth is easy if I know which class first defined the property. For my purposes, wherever a property first gained published visibility is where it first appeared.

I am using Delphi 2007. Please let me know if more detail is required. All help will be appreciated.

+3  A: 

This works for me.
The crux is getting the parent's TypeInfo from the passed through child TypeInfo

procedure InheritanceLevel(AClassInfo: PTypeInfo; const AProperty: string; var level: Integer);
var
  propInfo: PPropInfo;
  propCount: Integer;
  propList: PPropList;
  ix: Integer;
begin
  if not Assigned(AClassInfo) then Exit;
  propCount := GetPropList(AClassInfo, propList);
  for ix := 0 to propCount - 1 do
  begin
    propInfo := propList^[ix];
    if propInfo^.Name = AProperty then
    begin
      Inc(level);
      InheritanceLevel(GetTypeData(AClassInfo).ParentInfo^, AProperty, level)
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  level: Integer;
begin
  level := 0;
  InheritanceLevel(PTypeInfo(TForm.ClassInfo), 'Tag', level);
end;
Lieven
Ah... I see. Was hoping for a more direct way (e.g messing with the VMT), but this definitely works. Luckily efficiency is not my primary concern at the moment. Thanks for the quick answer - your solution is definitely outside the *direct* box I was stuck in.
Alan G.
+2  A: 

I don't know if you can find this using the RTTI available in Delphi 2007. Most properties in the TComponent tree are declared as protected in the original class, and then redeclared as published further down, and you only have RTTI for published members.

I was right about to describe something very similar to Lieven's solution when I saw that he'd beat me to it. This will find the first class where the property was published, if that's what you're looking for, but it won't find where the property was originally declared. You need Delphi 2010's extended RTTI if you wanted that.

Mason Wheeler
I forgot about the **published** part, you are right offcourse. Bottom line: it's not possible to get the class where the property was originally declared with Delphi 2007 using RTTI **if** the property was not originally declared in the published section.
Lieven
Right, regarding protected vs published, that's okay. I only used TControl as an example. I need this to work on a big tree of custom components. All of them have many published properties and often very deep inheritance levels, but none of them change property visibility. As far as I am concerned, wherever a property first gained published visiblity is where it first appeared. Hopefully, I can follow this rule to keep things simple.
Alan G.