【问题标题】:RTTI: how to get the object pointer of a field?RTTI:如何获取字段的对象指针?
【发布时间】:2016-11-01 21:46:51
【问题描述】:

我有一个名为aRttiPropertyTRttiProperty 变量,它指向下面的属性:

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

现在,如何从aRttiProperty 中提取fBilling 对象指针?

我尝试这样做,但它不起作用:

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

它返回父 TSubscription 对象而不是 fbilling 字段对象。

【问题讨论】:

  • 好吧,你可以使用Result := MySubscriptionInstance.Billing; 代替。
  • @Remy - 我的猜测是 Loki 想写信给这个领域。
  • @SertacAkyuz:如果他需要fBilling 字段本身的内存地址,则通过TRttiProperty 不是正确的方法(除非您将其类型转换为TRttiInstanceProperty 和然后解码其PropInfo^.SetProc 值)。更好的选择是使用aRttiType.GetField('fBilling').Offset(其中aRttiType 表示TSubscription 类)获取Tsubscription 类中fBilling 字段的偏移量,然后将该偏移量添加到Tsubscription 对象。
  • 还有FieldAddress取字段名,返回一个简单的指针,可以强制转换为TMyObject
  • @UweRaabe:TObject.FieldAddress() 方法仅适用于声明为 published 的字段。在 OP 的示例中,fBilling 被声明为private,因此SomeSubscriptionObject.FieldAddress('fBilling') 将返回nil

标签: delphi rtti


【解决方案1】:

您在问题中显示的代码非常好(前提是您修复了 Tsubscription 类声明以包含 getfBillingObj() 方法)。您显示的getfBillingObj() 代码返回正确的对象指针,如下代码所示:

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;

话虽如此,在这种情况下没有必要使用 RTTI,因为TSubscription 可以直接访问自己的内部字段:

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

但即使这样也是多余的,因为 billing 属性是公共的。任何调用者都可以按原样使用billing 属性:

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;

【讨论】:

  • 我没有意识到试图获取指针的代码正是在以它为字段的类中。使 RTTI 变得相当多余..
  • 谢谢雷米!完美的答案...由于您确认 aRttiProperty.GetValue(Self).AsType; 我在我的代码中发现了错误是好方法 :) 事实上我在做 Fbilling := TMyObject(Self);而不是 Fbilling := TMyObject.create(Self); ....愚蠢的“视觉”错误:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多