2016-06-10 24 views
7

Tôi đang sử dụng Delphi 2007 để duy trì một dự án cũ, tôi gặp vấn đề khi truy cập các hằng số lớp từ biến Class Reference, tôi luôn nhận được hằng số lớp cha thay vì con .Truy cập vào các hằng số lớp từ một biến tham chiếu lớp trong Delphi

Giả sử có lớp cha, một số lớp con, tham chiếu lớp và cuối cùng là mảng const để lưu trữ tham chiếu lớp cho mục đích lặp.

hãy xem sau chương trình đơn giản:

program TestClassConst; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

type 

    TParent = class 
    const 
    ClassConst = 'BASE CLASS'; 
    end; 

    TChild1 = class(TParent) 
    const 
    ClassConst = 'CHILD 1'; 
    end; 

    TChild2 = class(TParent) 
    const 
    ClassConst = 'CHILD 2'; 
    end; 

    TParentClass = class of TParent; 
    TChildClasses = array[0..1] of TParentClass; 

const 
    ChildClasses: TChildClasses = (TChild1, TChild2); 

var 
    i: integer; 
    c: TParentClass; 
    s: string; 

begin 
    try 
    writeln; 

    writeln('looping through class reference array'); 
    for i := low(ChildClasses) to high(ChildClasses) do begin 
     c := ChildClasses[i]; 
     writeln(c.ClassName, ' -> ', c.ClassConst); 
    end; 

    writeln; 

    writeln('accessing classes directly'); 
    writeln(TChild1.ClassName, ' -> ', TChild1.ClassConst); 
    writeln(TChild2.ClassName, ' -> ', TChild2.ClassConst); 

    except 
    on E: Exception do 
     Writeln(E.Classname, ': ', E.Message); 
    end; 
end. 

Khi nó chạy tôi nhận được:

looping through class reference array 
TChild1 -> BASE CLASS 
TChild2 -> BASE CLASS 

accessing classes directly 
TChild1 -> CHILD 1 
TChild2 -> CHILD 2 

tôi mong đợi để xem 'CON 1' và 'CON 2' cũng trong vòng lặp mảng!

Bất cứ ai có thể giải thích cho tôi lý do tại sao nó không hoạt động với tham chiếu lớp học?

+1

Bạn cần một phương pháp ảo để thực hiện đa hình . . – kludg

Trả lời

7

Hằng số lớp không định dạng là hằng số bình thường với một số phạm vi được thêm vào.
Hằng số lớp đã nhập thực sự là biến lớp mà bạn không thể thay đổi.
Vấn đề là các biến lớp không phải là ảo.

Hallvard Vassbotn đã viết về vấn đề này ở đây: Part 1, Part 2

Bạn không thể truy cập các biến lớp và hằng số lớp từ một tài liệu tham khảo lớp bởi vì ngôn ngữ không có hỗ trợ cho các biến lớp học ảo.
Khi bạn nói s:= TClass1.SomeConst trình biên dịch dịch thành s:= SomeGlobalButHiddenConst trước khi tiếp tục với phần còn lại của quá trình biên dịch.

class varclass const không gì khác ngoài đường cú pháp.
Vì vậy, liên kết giữa class var/const và lớp thực tế chỉ tồn tại trong thời gian biên dịch, nó bị hỏng đến thời gian chạy, giống như kiểu xóa trong Java.

RTTI cũng không giúp: Get constant fields from a class using RTTI
Tôi đoán nếu bạn đang sử dụng D2007 lựa chọn duy nhất của bạn là để khai báo một hàm ảo trả về hằng số bạn muốn:

tùy chọn D2010 Pre: phương pháp ảo

TParent = class 
    class function Name: string; virtual; 
end; 

TChild1 = class(TParent) 
    class function name: string; override; 
.... 
class function TParent.name: string; 
begin 
    Result:= Self.ClassConst; 
end; 

class function TChild1.name: string; 
begin 
    Result:= Self.ClassConst; //Silly copy paste solution 
end; 

Đây là trạng thái buồn, nhưng tôi không thấy tùy chọn khác.

From Delphi 2010 onwards: sử dụng attributes
Một lựa chọn tốt hơn là sử dụng các thuộc tính, các bạn có thể truy cập bằng RTTI:

Các mã sau hoạt động:

program TestClassConst; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, rtti; 

type 

    NameAttribute = class(TCustomAttribute) 
    private 
    Fname: string; 
    public 
    constructor Create(const Name: string); 
    property Name: string read Fname; 
    end; 

    [Name('Base class')] 
    TParent = class 
    const 
    ClassConst = 'BASE CLASS'; 
    private 
    public 
    class function Name: string; 
    end; 

    [Name('Child 1')] 
    TChild1 = class(TParent) 
    const 
    ClassConst = 'CHILD 1'; 
    end; 

    [Name('Child 2')] 
    TChild2 = class(TParent) 
    const 
    ClassConst = 'CHILD 2'; 
    end; 

    TParentClass = class of TParent; 
    TChildClasses = array[0..1] of TParentClass; 

const 
    ChildClasses: TChildClasses = (TChild1, TChild2); 

var 
    i: integer; 
    c: TParentClass; 
    s: string; 

{ TParent } 

class function TParent.Name: string; 
var 
    Context: TRttiContext; 
    ClassData: TRttiType; 
    Attr: TCustomAttribute; 
begin 
    Context:= TRttiContext.Create; 
    ClassData:= Context.GetType(Self); 
    try 
    for Attr in ClassData.GetAttributes do begin 
     if Attr is NameAttribute then Result:= NameAttribute(Attr).Name; 
    end; 
    finally 
    ClassData.Free; 
    end; 
end; 

{ NameAttribute } 

constructor NameAttribute.Create(const Name: string); 
begin 
    inherited Create; 
    FName:= name; 
end; 

begin 
    writeln; 

    writeln('looping through class reference array'); 
    for i := low(ChildClasses) to high(ChildClasses) do begin 
    c := ChildClasses[i]; 
    writeln(c.ClassName, ' -> ', c.Name); 
    end; 

    writeln; 

    writeln('accessing classes directly'); 
    writeln(TChild1.ClassName, ' -> ', TChild1.Name); 
    writeln(TChild2.ClassName, ' -> ', TChild2.Name); 
    readln; 
end. 
+0

cảm ơn Johan, giải pháp chức năng lớp được phân lớp hoạt động như một sự quyến rũ. Rất thú vị cũng là bài đăng Hallvard Vassbotn. Tôi không thể tưởng tượng đây là một vấn đề quá cũ .. và vẫn chưa được giải quyết! : D – MtwStark

+1

@MtwStark, bây giờ chúng ta có thuộc tính vấn đề phần lớn đã biến mất. – Johan

Các vấn đề liên quan