zeus
zeus

Reputation: 13345

RTTI: how to get the object pointer of a field?

I have a TRttiProperty variable named aRttiProperty, that points to the property below:

Tsubscription = class(TMyObject)
private
  fBilling: TMyObject;
public
  property billing: TMyObject read fBilling; // << aRttiProperty point to this member
end;

Now, how can I extract the fBilling object pointer from aRttiProperty?

I try to do it like this, but it is not working:

function Tsubscription.getfBillingObj(const aRttiProperty: TRttiProperty): TMyObject 
begin
  Result := aRttiProperty.GetValue(Self).AsType<TMyObject>;
end; 

It's returning the parent TSubscription object instead of the fbilling field object.

Upvotes: 0

Views: 1923

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595295

The code you showed in your question is perfectly fine (provided you fix your Tsubscription class declaration to include the getfBillingObj() method). The getfBillingObj() code you showed returns the correct object pointer, as demonstrated by the following code:

uses
  System.Rtti;

type
  TMyObject = class
  public
    Name: string;
    constructor Create(const aName: string);
  end;

  Tsubscription = class(TMyObject)
  private
    fBilling: TMyObject;
  public
    constructor Create(const aName: string);
    destructor Destroy; override;
    function getfBillingObj(const aRttiProperty: TRttiProperty): TMyObject;
    property billing: TMyObject read fBilling;
  end;

constructor TMyObject.Create(const aName: string);
begin
  inherited Create;
  Name := aName;
end;

constructor Tsubscription.Create(const aName: string);
begin
  inherited Create(aName);
  fBilling := TMyObject.Create('bill');
end;

destructor Tsubscription.Destroy;
begin
  fBilling.Free;
end;

function Tsubscription.getfBillingObj(const aRttiProperty: TRttiProperty): TMyObject;
begin
  Result := aRttiProperty.GetValue(Self).AsType<TMyObject>;
end;

var
  Ctx: TRttiContext;
  prop: TRttiProperty;
  sub: Tsubscription;
  bill: TMyObject;
begin
  sub := Tsubscription.Create('sub');
  try
    prop := ctx.GetType(Tsubscription).GetProperty('billing');
    bill := sub.getfBillingObj(prop);
    // bill.Name is 'bill' as expected...
  finally
    sub.Free;
  end;
end;

That being said, it is not necessary to use RTTI in this situation since TSubscription has direct access to its own internal fields:

function TSubscription.getfBillingObj: TMyObject 
begin
  Result := fBilling;
end; 

But even that is redundant since the billing property is public. Any caller can just use the billing property as-is:

var
  sub: Tsubscription;
  bill: TMyObject;
begin
  sub := Tsubscription.Create('sub');
  try
    bill := sub.billing;
    // bill.Name is 'bill' as expected...
  finally
    sub.Free;
  end;
end;

Upvotes: 6

Related Questions