从 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 varclass 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 中的类引用变量访问类常量的主要内容,如果未能解决你的问题,请参考以下文章

如何将另一个文件中的变量分配给类常量?

从 C++ 中的类访问私有变量

Java权限修饰符

Java权限修饰符

如何访问其子类中的类的私有变量?

在 Javascript 中访问事件处理程序中的类成员变量