Delphi appelant un constructeur virtuel basé sur le type TObject
J'ai un objet dérivé de l'objet TStringList que j'appelle un "TAutoString". Il vous permet de spécifier un type d'objet lors de la création de la liste. Ensuite, chaque fois qu'une nouvelle entrée est ajoutée à la liste de chaînes, elle crée également une copie de l'objet associé à cette entrée de chaîne. Cela facilite le stockage de toutes sortes d'informations supplémentaires avec chaque chaîne. Par exemple:
type TMyObject = class(TObject)
public
Cats: integer;
Dogs: integer;
Mice: integer;
end;
MO := TAutoString.Create(TMyObject);
À l'intérieur de l'objet, les informations de classe sont stockées dans une variable de classe:
private
ObjectClass: TClass;
constructor TAutoString.Create(ObjClass: TClass);
begin
inherited Create;
ObjectClass:=ObjClass;
end;
Désormais, chaque fois qu'un nouvel élément est ajouté, il crée un nouvel objet du type spécifié:
function TAutoString.Add(const S: string): Integer;
begin
Result:=inherited Add(S);
Objects[Result]:=ObjectClass.Create;
end;
Je peux maintenant ajouter ou lire des informations associées à chaque entrée de chaîne.
TMyObject(MO.Objects[25]).Cats := 17;
D:=TMyObject(MO.Objects[25]).Dogs;
Cela fonctionne très bien tant que l'objet n'a pas de constructeur. Si l'objet a un constructeur, son constructeur ne sera pas appelé lorsque l'objet est créé car le constructeur de TObject n'est pas virtuel.
Quelqu'un peut-il penser à un moyen de contourner ce problème. J'ai vu des solutions qui utilisent les bibliothèques RTTI, mais c'est dans Delphi-7, qui n'a pas de bibliothèque RTTI.
En passant, il semble un peu étrange que le constructeur de TObject ne soit pas virtuel. Si c'était le cas, cela activerait toutes sortes de fonctionnalités utiles comme celle que j'essaie d'implémenter.
EDIT: La suggestion de Remy ci-dessous était juste le coup de pouce dont j'avais besoin. J'avais initialement essayé une stratégie similaire, mais je ne pouvais pas la faire fonctionner. Quand cela ne semblait pas fonctionner comme je le pensais, j'ai supposé qu'il devait y avoir quelque chose que je ne comprenais pas à propos des méthodes virtuelles. Son message m'a poussé à le revoir. Il s'est avéré que j'avais laissé la directive "Override" pour le constructeur de l'objet que je voulais attacher. Maintenant, cela fonctionne comme il se doit.
L'autre problème qui m'inquiétait était que j'avais déjà utilisé les chaînes automatiques dans un tas d'autres applications où l'objet était basé sur "TObject" et je ne voulais pas revenir en arrière et changer tout ce code. J'ai résolu ce problème en surchargeant les constructeurs et en en ayant un pour les objets basés sur TObject et un autre pour mes objets TAutoClass:
constructor Create(ObjClass: TAutoClass); overload; virtual;
constructor Create(ObjClass: TClass); overload; virtual;
Selon le constructeur appelé, la classe d'objets stockée dans un différent dans une variable différente.
private
AutoClass: TAutoClass;
ObjectClass: TClass;
Ensuite, lorsque l'objet est construit, je vérifie ce qui a été attribué et je l'utilise:
procedure TAutoString.CreateClassInstance(Index: integer);
begin
if AutoClass<>nil then Objects[Index]:=AutoClass.Create
else Objects[Index]:=ObjectClass.Create
end;
La nouvelle version fonctionne parfaitement avec l'un ou l'autre type d'objet.
Réponses
Pour faire ce que vous voulez, vous devrez définir une classe de base pour les objets de votre liste à dériver, puis vous pourrez ajouter un constructeur virtuel à cette classe. Votre ObjectClassmembre devra utiliser ce type de classe au lieu d'utiliser TClass.
Par exemple:
type
TAutoStringObject = class(TObject)
public
constructor Create; virtual;
end;
TAutoStringObjectClass = class of TAutoStringObject;
TAutoString = class(TStringList)
private
ObjectClass: TAutoStringObjectClass;
public
constructor Create(ObjClass: TAutoStringObjectClass);
function Add(const S: string): Integer; override;
...
end;
...
constructor TAutoStringObject.Create;
begin
inherited Create;
end;
constructor TAutoString.Create(ObjClass: TAutoStringObjectClass);
begin
inherited Create;
ObjectClass := ObjClass;
end;
function TAutoString.Add(const S: string): Integer;
var
Obj: TAutoStringObject;
begin
Obj := ObjectClass.Create;
try
Result := inherited AddObject(S, Obj);
except
Obj.Free;
raise;
end;
end;
...
Ensuite, vous ajustez simplement vos classes d'objets dérivés à utiliser à la TAutoStringObjectplace de TObject, par exemple:
type
TMyObject = class(TAutoStringObject)
public
...
constructor Create; override;
end;
MO := TAutoString.Create(TMyObject);
...
Et leur constructeur sera appelé, comme prévu.