2012-07-05 19 views
5

Sử dụng Delphi 2010 và RTTI, tôi biết cách lấy loại lớp của đối tượng và cách lấy/đặt giá trị và loại thuộc tính của đối tượng, nhưng làm cách nào để xác định lớp nào trong chuỗi thừa kế một tài sản đến từ đâu? Tôi muốn sử dụng các thuộc tính của một lớp cơ sở khác với lớp chính.Delphi RTTI: Nhận lớp thuộc tính

xem xét mã này:

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; 

Vấn đề là không có GetClassType cho các thuộc tính. ClassType chỉ trả về TRttiInstancePropertyEx thay vì tên của lớp mà thuộc tính đó thuộc về.

+1

Câu hỏi của bạn hơi khó hiểu. Hãy làm rõ. Chính xác thì bạn đang tìm điều gì? Bạn đang cố gắng xác định nếu 'Obj.PropertyName' trả về một đối tượng là một cá thể' TClassBase' so với một cá thể 'TClassDescendant'? Hoặc là bạn đang cố gắng để xác định nếu 'Obj.PropertyName' chính nó được khai báo là' TClassBase' bất kể loại lớp thể hiện đối tượng trả về thực hiện? 'TClassBase' và' TClassDescendant' được sử dụng bởi (các) đối tượng bạn đang kiểm tra như thế nào? –

+0

Tôi muốn biết "làm cách nào để bạn xác định lớp nào trong chuỗi thừa kế một thuộc tính đến từ" hoặc đúng hơn là thuộc tính trong TClassBase hoặc trong TClassDescendant. Khi tôi đang đi qua các thuộc tính của một lớp, tôi muốn bỏ qua các thuộc tính lớp cơ sở. Trong tình huống cụ thể của tôi, tôi đã xuống lớp từ TInterfacedObject và đang thực hiện một chức năng trên tất cả các thuộc tính trừ khi chúng có thuộc tính [Ignore], nhưng tôi cũng muốn dễ dàng bỏ qua RefCount từ TInterfacedObject. –

+0

Thay vì kiểm tra xem thuộc tính hiện tại có tồn tại trong một lớp cụ thể hay không, nó sẽ có ý nghĩa hơn để kiểm tra xem đối tượng được liệt kê có phải là lớp dự định hay không. Điều đó sẽ dễ thực hiện hơn và chính xác hơn. –

Trả lời

5

lựa chọn khác là sử dụng Parent tài sản của TRttiProperty, từ đây bạn có thể truy cập vào các lớp đó bất động sản là một phần của.

{$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

Hoàn hảo! Chính xác những gì tôi cần, chỉ không biết làm thế nào để đạt được điều đó. Cảm ơn. –

2

Tôi không biết nếu nó có thể để có được những lớp học mà một tài sản đã được giới thiệu, nhưng bạn có thể giải quyết vấn đề của bạn với RTTI thường xuyên:

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

Tôi không nghĩ rằng đó là làm điều tương tự mà người dùng yêu cầu. Bạn đang kiểm tra bản thân lớp TClassBase để xem nó có một thuộc tính đã cho hay không, nhưng tôi nghĩ người dùng đang yêu cầu làm thế nào để kiểm tra xem một thuộc tính của một lớp khác là một cá thể TClassBase hay một cá thể con cháu thay thế. –

+0

@Remy - Mã giả định trong câu hỏi kiểm tra xem một thuộc tính liệt kê đã được giới thiệu trong 'TClassBase' chưa. Ít nhất đó là những gì tôi hiểu. Bạn có thể rất tốt ngay cả .. –

+1

Tôi vừa thử nó và điều này giải quyết vấn đề là tốt. Cảm ơn! –

2

Bạn có thể sử dụng phương pháp GetDeclaredProperties để tính declarated trong lớp hiện tại và sau đó so sánh với các giá trị được trả về theo phương thức GetProperties.

Thử mẫu này.

{$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

Vâng, tôi có thể thấy nó hoạt động như thế nào - đó là chặng đường dài. –