Delphi: Comment cacher ancêtres constructeurs?
-
28-09-2019 - |
Question
Mise à jour: éviscérés la question avec un exemple simple, que vous ne répondez pas par la réponse initialement acceptée
Compte tenu de la classe suivante, et son ancêtre:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
En ce moment TCellPhone
a 3 constructeurs visibles:
- Coupe: Entier
- Coupe: Integer; Teapot: string
- Teapot: string = ''
Que dois-je faire pour TCellPhone
afin que le constructeur ancêtre (de Teapot: string = ''
) n'est pas visible, ne laissant que les constructeurs ont déclaré:
- Coupe: Entier
- Coupe: Integer; Teapot: string
Remarque : En général, le simple fait de ayant un descendant constructeur se cache l'ancêtre:
TCellPhone = class(TComputer) public constructor Create(Cup: Integer); virtual; end;
- Coupe: Entier
Et si vous voulait pour garder le haut constructeur ancêtre et la descendant, vous marquerait la descendant en tant que
overload
:TCellPhone = class(TComputer) public constructor Create(Cup: Integer); overload; virtual; end;
- Coupe: Entier
- Teapot: string = ''
Dans le code exemple de cette question, Delphi confond mes mots-clés overload
:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
de penser que:
- je veux surcharger mes constructeurs avec ancêtre ,
- quand je veux vraiment surcharger avec frères et soeurs
Comment cacher le constructeur ancêtre?
Remarque: Il pourrait être impossible de cacher l'ancêtre, non-virtuelle, constructeur en utilisant le langage Delphi tel qu'il est actuellement défini. "Pas possible" est une réponse valable.
Tentative Réponse (échec)
i essayé marquer les constructeurs descendant avec reintroduce
(retombant à mon mode de mots-clés en ajoutant au hasard jusqu'à ce que cela fonctionne):
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); reintroduce; overload; virtual;
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
Mais cela ne fonctionne pas, les trois constructeurs sont encore visibles. : (
Original Question
J'ai un objet qui descend d'une classe qui a les constructeurs ne veulent pas voir:
TEniac = class(TObject)
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create
TComputer = class(TEniac) ...
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)
TCellPhone = class(TComputer)
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)
Remarque: Ceci est un exemple hypothétique. Comme dans le monde réel, les objets ancêtres ne peuvent être modifiés sans casser le code existant.
lors de l'utilisation de TiPhone
de quelqu'un que je ne veux pas les même pouvoir voir le constructeur de TEniac
:
iphone := TiPhone.Create(powerCord);
Pire encore: si elles appellent ce constructeur, ils passent complètement à côté de mon constructeur, et tout fait entre les deux. Il est assez facile d'appeler le constructeur mauvais, tous sont visibles dans l'IDE achèvement de code, et compilera:
TiPhone.Create;
et ils obtiennent un objet complètement invalide.
i pourrait changer TCellPhone
de lancer une exception dans ces constructeurs:
TCellPhone.Create(PowerCord: TPowercord)
begin
raise Exception.Create('Don''t use.');
end;
Mais les développeurs ne se rendent compte qu'ils appellent le mauvais constructeur jusqu'à ce que le client trouve l'erreur un jour et nous amendes bazillions de dollars. En fait, je suis essayer pour trouver partout que j'appelle le mauvais constructeur - mais je ne peux pas comprendre comment faire Delphi me dire
La solution
Il est impossible de faire jamais un constructeur introduit dans un ancêtre inaccessible pour la création d'une classe dérivée en Delphi parce que vous pouvez toujours faire ceci:
type
TComputerClass = class of TComputer;
var
CellPhoneClass: TComputerClass = TCellPhone;
CellPhone : TCellPhone;
begin
CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;
Rien que vous pourriez faire dans le code d'une classe dérivée serait jamais en mesure d'empêcher quiconque d'appeler le constructeur TComputer.Create pour créer une instance de la classe dérivée.
Le mieux que vous pouvez faire est:
TComputer = class(TObject)
public
constructor Create(Teapot: string=''); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string=''); overload; override;
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Dans ce cas, le code ci-dessus serait au moins appeler TCellPhone.Create(Teapot: string='')
au lieu de TComputer.Create(Teapot: string='')
Autres conseils
Si je me souviens bien, puis reintroduce
devrait aider virtuel méthodes.
reintroduce directive supprime les avertissements du compilateur sur le masquage des méthodes virtuelles déclaré précédemment. Utilisez reintroduce lorsque vous souhaitez masquer une méthode virtuelle héritée par une nouvelle.
Pour répondre à votre question mise à jour - Je pense que ce n'est pas possbile de cacher un non virtuel constructeur avec une surcharge dans une classe dérivée directement, mais j'ai essayé avec succès le suivant:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TIndermediateComputer = class(TComputer)
protected
// hide the constructor
constructor Create;
end;
TCellPhone = class(TIndermediateComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Vous ne pouvez pas cacher le constructeur de la classe mère à moins qu'elle a été déclarée virtuelle ou dynamique. Vous pouvez toutefois l'empêcher d'être appelé de la classe des enfants. Considérez votre exemple:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
TComputer.Create
sera toujours visible de TCellPhone
. Vous pouvez empêcher TComputer.Create
d'être appelé par mégarde en déclarant un TCellPhone.Create
avec la même signature.
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string='');
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Alors, tant que vous ne disposez pas d'un appel à inherited
dans le corps de TCellPhone.Create(Teapot: string='')
vous pouvez empêcher TComputer.Create
d'être appelé dans TCellPhone
et ses descendants. Ce qui suit:
TCellphone.Create;
TCellphone.Create('MyPhone');
résoudront à la mise en œuvre de TCellPhone.
De plus:
TiPhone = class(TCellPhone)
constructor Create;
end;
constructor TiPhone.Create;
begin
inherited;
end;
invoquera TCellPhone.Create
et non TComputer.Create
.
Au lieu de seulement élever une « Ne pas utiliser » exception dans les constructeurs invalides redéfinies, pensez à les marquer Obsolète dans la classe où ils deviennent invalides. Cela devrait produire des avertissements agréables du compilateur lorsque ces constructeurs invalides sont utilisés erroneosly.
TCellPhone = class(TComputer)
constructor Create(PowerCord: TPowerCord=nil); deprecated;
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
En outre, l'utilisation de remplacement ou rétablir si nécessaire.
Vous voulez réintroduire le constructeur:
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); reintroduce;
Voir TComponent.Create
dans le code source Delphi pour un exemple réel de cela.
Je sais que cela est de 5 ans le sujet, mais il peut aider quelqu'un. La seule façon de cacher le constructeur de l'ancêtre est de renommer l'une des deux méthodes de création à autre chose et de supprimer la nécessité de surcharge directive. Il fait un peu bizarre mais c'est la seule façon. Au moins dans les anciennes versions de Delphi. Je ne sais pas, il est désormais possible dans les versions XE xxx.