Accessing class constants from a class control variable in Delphi

I use Delphi 2007 to support an old project, I have a problem with accessing the class constants from the Reference variable of the class, I always get the constant of the parent class instead of the child one.

Suppose a parent class, some child classes, a class reference, and finally, a const array for storing class references for loop purposes.

take a look at the following simple program:

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. 

When it starts, I get:

 looping through class reference array TChild1 -> BASE CLASS TChild2 -> BASE CLASS accessing classes directly TChild1 -> CHILD 1 TChild2 -> CHILD 2 

I expected to see "CHILD 1" and "CHILD 2" also in an array loop!

Can someone explain to me why this does not work with class reference?

+7
inheritance const delphi class-constants
source share
1 answer

An uninitialized class constant is a normal constant with the addition of some scope.
The typed class constant is really a class variable that you cannot change.
The problem is that class variables are not virtual.

Halvard Wassbotn wrote about it here: Part 1 , Part 2

You cannot access class variables and class constants from a class reference, because the language does not support virtual class variables.
When you say s:= TClass1.SomeConst , the compiler translates this to s:= SomeGlobalButHiddenConst before moving on to the rest of the compilation.

class var and class const are nothing more than syntactic sugar.
Thus, the connection between class var/const and the actual class exists only at compile time, it is interrupted at runtime, like erasing styles in Java.

RTTI also doesn't help: Get persistent fields from a class using RTTI
I think if you are using D2007, your only option is to declare a virtual function that returns the constant you want:

Pre D2010 Parameter: Virtual Method

 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; 

This is a sad state of affairs, but I do not see another option.

From Delphi 2010 onwards : use attributes
It is better to use attributes that you can access using RTTI :

The following code works:

 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. 
+7
source share

All Articles