Mtw*_*ark 8 delphi inheritance const class-constants
我正在使用Delphi 2007来维护一个旧项目,我在从Class Reference变量访问类常量时遇到问题,我总是得到父类常量而不是子类常量.
假设有一个父类,一些子类,一个类引用,最后是一个const数组来存储类引用以进行循环.
看看以下简单的程序:
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.
Run Code Online (Sandbox Code Playgroud)
当它运行我得到:
looping through class reference array
TChild1 -> BASE CLASS
TChild2 -> BASE CLASS
accessing classes directly
TChild1 -> CHILD 1
TChild2 -> CHILD 2
Run Code Online (Sandbox Code Playgroud)
我希望在阵列循环中看到'CHILD 1'和'CHILD 2'!
任何人都可以解释为什么它不适用于类引用?
非类型化的类常量是一个常规常量,并添加了一些作用域.
类型化常量实际上是一个无法更改的类变量.
问题是类变量不是虚拟的.
Hallvard Vassbotn在这里写到了这个问题:第1 部分,第2部分
您无法从类引用访问类变量和类常量,因为该语言不支持虚拟类变量.
当你说s:= TClass1.SomeConst编译器将其转换为s:= SomeGlobalButHiddenConst继续编译的其余部分之前.
class var而且class const只不过是语法糖.
因此class var/const,实际类之间的链接仅在编译时存在,它在运行时被破坏,就像Java中的类型擦除一样.
RTTI也没有帮助:使用RTTI从类中获取常量字段
我想如果您正在使用D2007,您唯一的选择是声明一个返回所需常量的虚函数:
Pre D2010选项:虚拟方法
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;
Run Code Online (Sandbox Code Playgroud)
这是一种悲惨的状况,但我没有看到另一种选择.
从Delphi 2010开始:使用属性
更好的选择是使用属性,您可以使用 RTTI访问这些属性:
以下代码有效:
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.
Run Code Online (Sandbox Code Playgroud)