首页 > 解决方案 > 如何访问元类引用的 TList 中的类变量?

问题描述

我有这样的课:

type
  TCipher = class
    private

    public
        function Encrypt(sText: String; Key: TObject): String; virtual; abstract;
        function Decrypt(sText: String; Key: TObject): String; virtual; abstract;
        class var
          sName: String;
          sDisplayName: String;
  end;
  TCipherClass = class of TCipher;
  
  TCaesar = class(TCipher)
    private

    public
        function Encrypt(sText: String; Key: TObject): String; override;
        function Decrypt(sText: String; Key: TObject): String; override;
        class var
          sName: String;
          sDisplayName: String;
  end;

implementation

function TCaesar.Encrypt(sText: String; Key: TObject): String;
begin
  
end;

function TCaesar.Decrypt(sText: String; Key: TObject): String;
begin
  
end;

initialization

  TCipher.sName := 'defaultName';
  TCipher.sDisplayName := 'defaultDipsplay';
  ShowMessage(TCipher.sName + '  ' + TCipher.sDisplayName);
  
  TCaesar.sName := 'caesar';
  TCaesar.sDisplayName := 'Caesar Cipher';
  ShowMessage(TCaesar.sName + '  ' + TCaesar.sDisplayName);

end.

在其他地方,我有这样TList的课程:

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.sDisplayName);
end;

但这会返回defDipsplay而不是Caesar Cipher. 我认为这是我在列表中做错的事情,因为当我直接从课堂上展示它时,它显示得很好:

ShowMessage(TCipher.ClassName + ' . ' + TCipher.sDisplayName);
ShowMessage(TCaesar.ClassName + ' . ' + TCaesar.sDisplayName);

标签: delphi

解决方案


您的变量被声明为class var,因此它们特定于声明它们的每个类。您class声明的变量与 中的变量TCaesar具有相同的名称,但是当您尝试在运行时通过元类引用访问这些变量时,编译器不知道元类引用的是哪个实际类,因此它只能在编译时做-time 是生成代码来访问-specific 变量,而不是-specific 变量。classTCipherTCipherClassTCipherTCaesar

在这种情况下,我建议使用class virtual方法而不是class vars,然后每个类可以override在运行时返回不同的值的方法。

type
  TCipher = class
  public
    function Encrypt(sText: String; Key: TObject): String; virtual; abstract;
    function Decrypt(sText: String; Key: TObject): String; virtual; abstract;
    class function CipherName: String; virtual;
    class function DisplayName: String; virtual;
  end;
  TCipherClass = class of TCipher;
  
  TCaesar = class(TCipher)
  public
    function Encrypt(sText: String; Key: TObject): String; override;
    function Decrypt(sText: String; Key: TObject): String; override;
    class function CipherName: String; override;
    class function DisplayName: String; override;
  end;

implementation

function TCaesar.Encrypt(sText: String; Key: TObject): String;
begin
  ...  
end;

function TCaesar.Decrypt(sText: String; Key: TObject): String;
begin
  ...  
end;

class function TCipher.CipherName: String; 
begin
  Result := 'defaultName';
end;

class function TCipher.DisplayName: String;
begin
  Result := 'defaultDipsplay';
end;

class function TCaesar.CipherName: String;
begin
  Result := 'caesar';
end;

class function TCaesar.DisplayName: String;
begin
  Result := 'Caesar Cipher';
end;

initialization
  ShowMessage(TCipher.CipherName + '  ' + TCipher.DisplayName);
  ShowMessage(TCaesar.CipherName + '  ' + TCaesar.DisplayName);

end.
var
  lstCiphers: TList<TCipherClass>;
  cipher: TCipherClass;
begin
  lstCiphers := TList<TCipherClass>.Create;
  lstCiphers.Add(TCaesar);

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

推荐阅读