Delphi RTTI: Get property's class
Asked Answered
T

3

7

Using Delphi 2010 and RTTI, I know how to get the class type of an object and how to get/set the value and type of an object's properties, but how do you determine which class in the inheritance chain a property came from? I want to use the properties of a base class differently than the main class.

Consider this code:

TClassBase = class(TObject)
published
  property A: Integer;
end;

TClassDescendant = class(TClassBase)
published
  property B: Integer;
end;

procedure CheckProperties(Obj: TObject);
var
  ctx: TRttiContext;
  objType: TRttiType;
  Prop: TRttiProperty;
begin
  ctx := TRttiContext.Create;
  objType := ctx.GetType(Obj.ClassInfo);
  for Prop in objType.GetProperties do begin
    if Prop.GetClassType is TClassBase then
      // do something special with base class properties
    else
      // standard functionality on all other properties
  end;
end;

The problem is there is no GetClassType for the properties. ClassType just returns TRttiInstancePropertyEx instead of the name of the class to which the property belongs.

Thaliathalidomide answered 5/7, 2012 at 18:43 Comment(4)
Your question is a little confusing. Please clearify. What EXACTLY are you looking for? Are you trying to determine if Obj.PropertyName returns an object that is a TClassBase instance versus a TClassDescendant instance? Or are you trying to determine if Obj.PropertyName itself is declared as TClassBase regardless of what class type the returned object instance implements? How are TClassBase and TClassDescendant being used by the object(s) you are checking?Blandish
I want to know "how do you determine which class in the inheritance chain a property came from" or rather is the property in TClassBase or in TClassDescendant. As I'm traversing the properties of a class, I want to ignore base class properties. In my particular situation, I've descended a class from TInterfacedObject and am performing a function on all properties unless they have an [Ignore] attribute, but I also want to easily ignore RefCount from TInterfacedObject.Thaliathalidomide
Rather than checking if the current property exists in a specific class, it would make more sense to check if the object being enumerated is the intended class or not. That would be much easier to implement and be more accurate.Blandish
@RemyLebeau, No, I'm using TClassDescendant and traversing it's properties, but since it inherits TClassBase, it brings those properties along with it. I didn't know how to figure out which class a property belonged to while looking at all the properties. I needed to ignore all properties that were declared in TInterfacedObject.Thaliathalidomide
J
7

Another option is use the Parent property of the TRttiProperty, from here you can access to the class which the property is part of.

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Rtti,
  SysUtils;

type
  TClassBase = class(TObject)
    private
      FA: Integer;
   published
    property A: Integer read FA;
  end;

  TClassDescendant = class(TClassBase)
    private
      FB: Integer;
    published
    property B: Integer read FB;
  end;

procedure CheckProperties(Obj: TObject);
var
  ctx: TRttiContext;
  objType: TRttiType;
  Prop: TRttiProperty;
begin
  ctx := TRttiContext.Create;
  objType := ctx.GetType(Obj.ClassInfo);
   for Prop in objType.GetProperties do
   if TRttiInstanceType(Prop.Parent).MetaclassType=TClassBase then
     Writeln(Format('The property %s is declarated in the TClassBase class',[Prop.Name]))
   else
     Writeln(Format('The property %s is not declarated in the TClassBase class',[Prop.Name]))
end;


begin
  try
   //CheckProperties(TClassBase.Create);
   CheckProperties(TClassDescendant.Create);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
Jesselton answered 5/7, 2012 at 21:50 Comment(1)
Perfect! Exactly what I needed, just didn't know how to get there. Thanks.Thaliathalidomide
G
3

I don't know if it's possible to get the class which a property was introduced, but you can solve your problem with regular RTTI:

begin
  ...

  for Prop in objType.GetProperties do begin
    if Assigned(GetPropInfo(TClassBase, Prop.Name)) then
      // do something special with base class properties
    else
      // standard functionality on all other properties
  end;
end;
Grundyism answered 5/7, 2012 at 20:29 Comment(2)
I don't think that is doing the same thing that the user asked for. You are checking the TClassBase class itself to see if it has a given property, but I think the user is asking for how to check if a property of another class is a TClassBase instance or a descendant instance instead.Blandish
@Remy - The hypothetical code in the question checks if an enumerated property has already been introduced in TClassBase. At least that's what I understood. You may be very well right though..Grundyism
J
3

You can use the GetDeclaredProperties method to get properties declarated in the current class and then compare against the values returned by the GetProperties method.

Try this sample.

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Rtti,
  SysUtils;

type
  TClassBase = class(TObject)
    private
      FA: Integer;
   published
    property A: Integer read FA;
  end;

  TClassDescendant = class(TClassBase)
    private
      FB: Integer;
    published
    property B: Integer read FB;
  end;

procedure CheckProperties(Obj: TObject);

  function ExistProp(const PropName:string; List:TArray<TRttiProperty>) : Boolean;
  var
   Prop: TRttiProperty;
  begin
   result:=False;
    for Prop in List do
     if SameText(PropName, Prop.Name) then
     begin
       Result:=True;
       break;
     end;
  end;

var
  ctx: TRttiContext;
  objType: TRttiType;
  Prop: TRttiProperty;
  CurrentClassProps : TArray<TRttiProperty>;
begin
  ctx := TRttiContext.Create;
  objType := ctx.GetType(Obj.ClassInfo);
  CurrentClassProps:=objType.GetDeclaredProperties;
   for Prop in objType.GetProperties do
   if ExistProp(Prop.Name, CurrentClassProps) then
     Writeln(Format('The property %s is declarated in the current %s class',[Prop.Name, obj.ClassName]))
   else
     Writeln(Format('The property %s is declarated in the base class',[Prop.Name]))
end;



begin
  try
   //CheckProperties(TClassBase.Create);
   CheckProperties(TClassDescendant.Create);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
Jesselton answered 5/7, 2012 at 21:43 Comment(1)
Yes, I can see how that would work--it's the long way around.Thaliathalidomide

© 2022 - 2024 — McMap. All rights reserved.