2012-07-05 38 views
5

Utilizando Delphi 2010 y RTTI, sé cómo obtener el tipo de clase de un objeto y cómo obtener/establecer el valor y tipo de las propiedades de un objeto, pero ¿cómo se determina qué clase en el cadena de herencia de donde vino una propiedad? Quiero usar las propiedades de una clase base de forma diferente que la clase principal.Delphi RTTI: Obtener clase de propiedad

consideran este código:

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; 

El problema es que no hay GetClassType de las propiedades. ClassType simplemente devuelve TRttiInstancePropertyEx en lugar del nombre de la clase a la que pertenece la propiedad.

+1

Tu pregunta es un poco confusa. Por favor, aclarar. ¿Que es exactamente lo que está buscando? ¿Estás tratando de determinar si 'Obj.PropertyName' devuelve un objeto que es una instancia' TClassBase' contra una instancia 'TClassDescendant'? ¿O está tratando de determinar si 'Obj.PropertyName' se declara como' TClassBase' independientemente del tipo de clase que implemente la instancia del objeto devuelto? ¿Cómo se usan 'TClassBase' y' TClassDescendant' para los objetos que está comprobando? –

+0

Quiero saber "cómo se determina en qué clase de la cadena de herencia proviene una propiedad" o más bien es la propiedad en TClassBase o en TClassDescendant. Como estoy atravesando las propiedades de una clase, quiero ignorar las propiedades de la clase base. En mi situación particular, he descendido una clase de TInterfacedObject y estoy realizando una función en todas las propiedades a menos que tengan un atributo [Ignore], pero también deseo ignorar fácilmente RefCount from TInterfacedObject. –

+0

En lugar de verificar si la propiedad actual existe en una clase específica, tendría más sentido verificar si el objeto enumerado es la clase deseada o no. Eso sería mucho más fácil de implementar y más preciso. –

Respuesta

5

Otra opción es utilizar la propiedad Parent del TRttiProperty, desde aquí se puede acceder a la clase que la propiedad es parte de.

{$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. 
+0

¡Perfecto! Exactamente lo que necesitaba, simplemente no sabía cómo llegar allí. Gracias. –

2

No sé si es posible conseguir la clase que se introdujo una propiedad, pero se puede resolver su problema con RTTI normal:

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; 
+0

No creo que esté haciendo lo mismo que el usuario solicitó. Está revisando la clase TClassBase para ver si tiene una propiedad determinada, pero creo que el usuario está preguntando cómo verificar si una propiedad de otra clase es una instancia TClassBase o una instancia descendiente. –

+0

@Remy - El código hipotético en la pregunta comprueba si una propiedad enumerada ya se ha introducido en 'TClassBase'. Al menos eso es lo que entendí. Aunque puede que estés muy bien. –

+1

Lo probé y esto resuelve el problema también. ¡Gracias! –

2

Usted puede utilizar el método GetDeclaredProperties obtener las propiedades declarated en la clase actual y luego compare con los valores devueltos por el método GetProperties.

Pruebe esta muestra.

{$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. 
+0

Sí, puedo ver cómo funcionaría eso: es el camino más largo. –

Cuestiones relacionadas