Delphi chiama un costruttore virtuale basato sul tipo TObject
Ho un oggetto derivato dall'oggetto TStringList che chiamo "TAutoString". Consente di specificare un tipo di oggetto quando viene creato l'elenco. Quindi ogni volta che viene aggiunta una nuova voce all'elenco delle stringhe, viene creata anche una copia dell'oggetto associato a quella voce della stringa. Ciò semplifica la memorizzazione di tutti i tipi di informazioni aggiuntive insieme a ciascuna stringa. Per esempio:
type TMyObject = class(TObject)
public
Cats: integer;
Dogs: integer;
Mice: integer;
end;
MO := TAutoString.Create(TMyObject);
All'interno dell'oggetto, le informazioni sulla classe sono memorizzate in una variabile di classe:
private
ObjectClass: TClass;
constructor TAutoString.Create(ObjClass: TClass);
begin
inherited Create;
ObjectClass:=ObjClass;
end;
Ora, ogni volta che viene aggiunto un nuovo elemento, crea un nuovo oggetto del tipo specificato:
function TAutoString.Add(const S: string): Integer;
begin
Result:=inherited Add(S);
Objects[Result]:=ObjectClass.Create;
end;
Ora posso aggiungere o leggere le informazioni associate a ciascuna voce di stringa.
TMyObject(MO.Objects[25]).Cats := 17;
D:=TMyObject(MO.Objects[25]).Dogs;
Funziona alla grande finché l'oggetto non ha un costruttore. Se l'oggetto ha un costruttore, il suo costruttore non verrà chiamato quando l'oggetto viene creato perché il costruttore per TObject non è virtuale.
Qualcuno può pensare a un modo per aggirare questo problema. Ho visto soluzioni che usano le librerie RTTI, ma questo è in Delphi-7, che non ha una libreria RTTI.
Per inciso, sembra un po 'strano che il costruttore di TObject non sia virtuale. Se lo fosse, abiliterebbe tutti i tipi di funzionalità utili come quella che sto cercando di implementare.
EDIT: Il suggerimento di Remy di seguito era solo la spinta di cui avevo bisogno. Inizialmente avevo provato una strategia simile, ma non riuscivo a farla funzionare. Quando non sembrava funzionare come pensavo, pensavo che ci fosse qualcosa che non capivo sui metodi virtuali. Il suo post mi ha spinto a guardarlo di nuovo. Si è scoperto che avevo omesso la direttiva "Override" per il costruttore dell'oggetto che volevo allegare. Ora funziona proprio come dovrebbe.
L'altro problema di cui ero preoccupato era che avevo già utilizzato le stringhe automatiche in un mucchio di altre applicazioni in cui l'oggetto era basato su "TObject" e non volevo tornare indietro e cambiare tutto quel codice. Ho risolto il problema sovraccaricando i costruttori e avendone uno per gli oggetti basati su TObject e un altro i miei oggetti TAutoClass:
constructor Create(ObjClass: TAutoClass); overload; virtual;
constructor Create(ObjClass: TClass); overload; virtual;
A seconda del costruttore chiamato, la classe dell'oggetto memorizzata in un diverso in una variabile diversa.
private
AutoClass: TAutoClass;
ObjectClass: TClass;
Quindi, quando l'oggetto è costruito, controllo per vedere quale è stato assegnato e lo uso:
procedure TAutoString.CreateClassInstance(Index: integer);
begin
if AutoClass<>nil then Objects[Index]:=AutoClass.Create
else Objects[Index]:=ObjectClass.Create
end;
La nuova versione funziona perfettamente con entrambi i tipi di oggetto.
Risposte
Per fare quello che vuoi, dovrai definire una classe base da cui derivare gli oggetti dell'elenco, quindi puoi aggiungere un costruttore virtuale a quella classe. Il tuo ObjectClassmembro dovrà usare quel tipo di classe invece di usare TClass.
Per esempio:
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;
...
Quindi, devi semplicemente regolare le classi di oggetti derivati da utilizzare TAutoStringObjectinvece di TObject, ad esempio:
type
TMyObject = class(TAutoStringObject)
public
...
constructor Create; override;
end;
MO := TAutoString.Create(TMyObject);
...
E il loro costruttore verrà chiamato, come previsto.