2016-06-10 18 views
7

Używam Delphi 2007 do zachowania starego projektu, mam problem z dostępem do stałych klas ze zmiennej klasy Reference, zawsze uzyskuję stałą klasę rodziców zamiast dzieci .Dostęp do stałych klas ze zmiennej klasy Reference w Delphi

Załóżmy, że mamy klasę nadrzędną, niektóre klasy potomne, odwołanie do klasy i wreszcie tablicę const do przechowywania referencji klas dla celów zapętlenia.

spojrzeć na następujące prostego programu:

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. 

Kiedy skończy uzyskać:

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

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

Spodziewałem się zobaczyć 'dziecko 1' i 'dziecko 2' również w pętli tablicy!

Czy ktoś może mi wyjaśnić, dlaczego nie działa z odniesieniem do klasy?

+1

Trzeba metodę wirtualną wdrożyć polimorfizm . . – kludg

Odpowiedz

7

Stała stała bez typu jest stałą normalną z dodanym pewnym zakresem.
Wpisana stała klasy jest naprawdę zmienną klasy, której nie można zmienić.
Problem polega na tym, że zmienne klasy nie są wirtualne.

Hallvard Vassbotn napisał na ten temat tutaj: Part 1, Part 2

Nie można uzyskać dostęp do zmiennych i stałych klasy klasy z odniesieniem klasy, ponieważ język nie posiada wsparcie dla zmiennych wirtualnych klas.
Kiedy mówisz s:= TClass1.SomeConst, kompilator tłumaczy to na s:= SomeGlobalButHiddenConst, zanim przejdzie do dalszej części kompilacji.

class var i class const są niczym więcej niż cukrem syntaktycznym.
Jako takie połączenie między class var/const a rzeczywistą klasą istnieje tylko podczas kompilacji, jest przerwane w czasie pracy, podobnie jak usuwanie typu w Javie.

RTTI także nie pomaga: Get constant fields from a class using RTTI
Myślę, że jeśli używasz D2007 jedynym rozwiązaniem jest zadeklarować wirtualny funkcję zwracającą stała chcesz:

Pre opcję D2010: metodę wirtualną

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; 

To smutny stan rzeczy, ale nie widzę innej opcji.

From Delphi 2010 onwards: Redakcyjne attributes
Lepszym rozwiązaniem jest użycie atrybutów, to można uzyskać dostęp za pomocą RTTI:

Poniższy kod działa:

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

dziękuję Johan, podklasy funkcji funkcji klasy działa jak urok. Bardzo interesujący również post Hallvard Vassbotn. Nie mogę sobie wyobrazić, że był to tak stary problem ... i wciąż nierozwiązany! : D – MtwStark

+1

@ MtwStark, teraz, gdy mamy atrybuty, problem w znacznej mierze zniknął. – Johan