I have a class like this:
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.
Elsewhere, I have a TList
of the classes like this:
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;
But this returns defDipsplay
instead of Caesar Cipher
. I think this is something I'm doing wrong with the list, because when I show it straight from the class it shows fine:
ShowMessage(TCipher.ClassName ' . ' TCipher.sDisplayName);
ShowMessage(TCaesar.ClassName ' . ' TCaesar.sDisplayName);
CodePudding user response:
Your variables are declared as class var
, so they are specific to each class that declares them. You have class
variables declared in TCaesar
that have the same names as class
variables in TCipher
, but when you try to access those variables at runtime via a TCipherClass
metaclass reference, the compiler doesn't know which actual class the metaclass refers to, so all it can do at compile-time is generate code to access the TCipher
-specific variables, not the TCaesar
-specific variables.
In this situation, I would suggest using class virtual
methods instead of class var
s, then each class can override
the methods to return different values at runtime.
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;
CodePudding user response:
You've redeclared sName
and sDisplayName
in TCaesar
so these are hiding the same-named variables in the ancestor TCipher
class. Just remove that declaration and you get the behaviour you expect.
TCaesar = class(TCipher)
private
public
function Encrypt(sText: String; Key: TObject): String; override;
function Decrypt(sText: String; Key: TObject): String; override;
end;
The sName
and sDisplayName
fields will still be members of TCaesar
here because they will be inherited from TCipher
. If you declare new variables with the same name in a descendent class then they will hide the ancestor variables when referenced directly via a reference of the descendent type. When using a variable that treats the object as an ancestor, however, you will read and write to the ancestor's fields instead.