2016-11-01 2 views
0

У меня есть TRttiProperty переменную с именем aRttiProperty, что указывает на свойство ниже:RTTI: как получить указатель объекта на поле?

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

Теперь, как я могу извлечь указатель fBilling объекта из aRttiProperty?

Я пытаюсь сделать это так, но это не работает:

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

Это вместо возвращения родительского объекта TSubscription объекта fbilling поля.

+0

Ну это просто перебор, когда вы можете просто использовать 'Result: = MySubscriptionInstance.Billing;' вместо этого. –

+0

@Remy - Думаю, Локи хочет написать на поле. –

+0

@SertacAkyuz: Если ему нужен адрес памяти самого поля 'fBilling', то через' TRttiProperty' это не правильный способ сделать это (если вы не наберете его в 'TRttiInstanceProperty', а затем декодируете его' PropInfo ^. SetProc'). Лучшим вариантом было бы получить смещение поля 'fBilling' в классе' Tsubscription' с помощью 'aRttiType.GetField ('fBilling'). Offset' (где' aRttiType' представляет класс 'TSubscription'), а затем добавьте это смещение к начальному адресу памяти объекта 'Tsubscription'. –

ответ

4

Код, который вы указали в своем вопросе, отлично (при условии, что вы исправили объявление класса 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; 
+0

Я не понимал, что код, пытающийся получить указатель, был в самом классе, который имеет его как свое поле. Renders RTTI довольно избыточно. –

+1

Спасибо, реми! отличный ответ ... я нашел ошибку в своем коде благодаря вашему подтверждению, что aRttiProperty.GetValue (Self) .AsType ; это хороший способ :) на самом деле я делал Fbilling: = TMyObject (Self); вместо Fbilling: = TMyObject.create (Self); .... глупая "визуальная" ошибка :( – loki

Смежные вопросы