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.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
David Cornelius
  • 437
  • 1
  • 5
  • 12
  • 1
    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? – Remy Lebeau Jul 05 '12 at 20:55
  • 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. – David Cornelius Jul 05 '12 at 22:59
  • 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. – Remy Lebeau Jul 05 '12 at 23:06
  • @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. – David Cornelius Jul 05 '12 at 23:12

3 Answers3

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.
RRUZ
  • 134,889
  • 20
  • 356
  • 483
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;
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • 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. – Remy Lebeau Jul 05 '12 at 20:43
  • @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.. – Sertac Akyuz Jul 05 '12 at 20:48
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.
RRUZ
  • 134,889
  • 20
  • 356
  • 483