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 vars, 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.
