You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pascal的元类引用TList中正确访问类变量?

问题原因

Object Pascal中的class var定义的类变量属于静态绑定成员,不具备多态特性。编译器会根据变量声明时的静态类型解析成员地址,而非运行时的实际类型:

  • 遍历用的循环变量cipher静态类型是TCipherClass(基类元类),所以编译阶段cipher.sDisplayName就被绑定到了TCipher的类变量上,运行时即使实际指向TCaesar的元类,也只会返回基类的值。
  • 直接写TCaesar.sDisplayName时,编译器明确知道静态类型是TCaesar,所以会正确解析到子类的类变量。
解决方案

推荐使用虚类方法替代直接暴露类变量,虚类方法支持动态绑定,会根据运行时的实际元类类型调用对应实现,修改代码如下:

类定义修改

type
  TCipher = class
    private
      // 把类变量改为私有,不对外直接暴露
      class var
        FsName: String;
        FsDisplayName: String;
    public
        function Encrypt(sText: String; Key: TObject): String; virtual; abstract;
        function Decrypt(sText: String; Key: TObject): String; virtual; abstract;
        // 新增虚类方法用于获取属性
        class function GetName: String; virtual;
        class function GetDisplayName: String; virtual;
  end;
  TCipherClass = class of TCipher;
  
  TCaesar = class(TCipher)
    private
      // 子类同名类变量也改为私有
      class var
        FsName: String;
        FsDisplayName: String;
    public
        function Encrypt(sText: String; Key: TObject): String; override;
        function Decrypt(sText: String; Key: TObject): String; override;
        // 重写基类的虚类方法
        class function GetName: String; override;
        class function GetDisplayName: String; override;
  end;

implementation

// 基类方法实现
class function TCipher.GetName: String;
begin
  Result := FsName;
end;

class function TCipher.GetDisplayName: String;
begin
  Result := FsDisplayName;
end;

// 子类方法实现
class function TCaesar.GetName: String;
begin
  Result := FsName;
end;

class function TCaesar.GetDisplayName: String;
begin
  Result := FsDisplayName;
end;

// 原有初始化逻辑保持不变
initialization
  TCipher.FsName := 'defaultName';
  TCipher.FsDisplayName := 'defaultDisplay';
  ShowMessage(TCipher.GetName + '  ' + TCipher.GetDisplayName);
  
  TCaesar.FsName := 'caesar';
  TCaesar.FsDisplayName := 'Caesar Cipher';
  ShowMessage(TCaesar.GetName + '  ' + TCaesar.GetDisplayName);

遍历逻辑修改

调用虚类方法即可正确获取对应子类的属性值:

var
  lstCiphers: TList<TCipherClass>;
  cipher: TCipherClass;
begin
  lstCiphers := TList<TCipherClass>.Create;
  lstCiphers.AddRange([TCaesar]);

  for cipher in lstCiphers do
    ShowMessage('In list: ' + cipher.ClassName + ' . ' + cipher.GetDisplayName);
end;

内容的提问来源于stack exchange,提问作者user15400843

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 09:54:02