从 Delphi 中的类引用变量访问类常量
Posted
技术标签:
【中文标题】从 Delphi 中的类引用变量访问类常量【英文标题】:Accessing Class Constants from a Class Reference variable in Delphi 【发布时间】:2016-10-11 12:45:54 【问题描述】:我正在使用 Delphi 2007 维护一个旧项目,我在从类引用变量访问类常量时遇到问题,我总是得到父类常量而不是子类常量。
假设有一个父类、一些子类、一个类引用,最后是一个 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.
当它运行时,我得到:
looping through class reference array
TChild1 -> BASE CLASS
TChild2 -> BASE CLASS
accessing classes directly
TChild1 -> CHILD 1
TChild2 -> CHILD 2
我希望在数组循环中也能看到“CHILD 1”和“CHILD 2”!
谁能解释一下为什么它不适用于类引用?
【问题讨论】:
你需要一个虚方法来实现多态性。 . 【参考方案1】:无类型的类常量是添加了一些作用域的普通常量。 类型化的类常量实际上是一个您无法更改的类变量。 问题是类变量不是虚拟的。
Hallvard Vassbotn 在这里写过这个问题:Part 1, Part 2
您无法从类引用中访问类变量和类常量,因为该语言不支持虚拟类变量。
当您说 s:= TClass1.SomeConst
时,编译器会将其转换为 s:= SomeGlobalButHiddenConst
,然后继续进行其余的编译。
class var
和 class const
只不过是语法糖。
因此,class var/const
和实际类之间的链接只存在于编译时,它在运行时被破坏,就像 Java 中的类型擦除一样。
RTTI 也无济于事:Get constant fields from a class using 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;
这是一种可悲的情况,但我看不到其他选择。
From Delphi 2010 onwards:使用attributes 更好的选择是使用属性,您可以使用 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.
【讨论】:
谢谢 Johan,子类化的类函数解决方案就像一个魅力。 Hallvard Vassbotn 的帖子也很有趣。我无法想象这是一个如此古老的问题.. 仍然没有解决! :D @MtwStark,现在我们有了属性,问题基本上已经解决了。以上是关于从 Delphi 中的类引用变量访问类常量的主要内容,如果未能解决你的问题,请参考以下文章