Delphi memanggil konstruktor virtual berdasarkan tipe TObject

Sep 12 2020

Saya memiliki objek yang diturunkan dari objek TStringList yang saya sebut "TAutoString." Ini memungkinkan Anda untuk menentukan tipe objek saat daftar dibuat. Kemudian setiap kali entri baru ditambahkan ke daftar string, itu juga membuat salinan objek yang terkait dengan entri string itu. Ini membuatnya mudah untuk menyimpan semua jenis informasi tambahan bersama dengan setiap string. Sebagai contoh:

type TMyObject = class(TObject)
 public
  Cats: integer;
  Dogs: integer;
  Mice: integer;
 end;

MO := TAutoString.Create(TMyObject);

Di dalam objek, informasi kelas disimpan dalam variabel kelas:

 private
  ObjectClass: TClass;


constructor TAutoString.Create(ObjClass: TClass);
begin
    inherited Create;
    ObjectClass:=ObjClass;
end;

Sekarang, setiap kali item baru ditambahkan, itu membuat objek baru dari tipe yang ditentukan:

function TAutoString.Add(const S: string): Integer;
begin
    Result:=inherited Add(S);
    Objects[Result]:=ObjectClass.Create;
end;

Sekarang saya dapat menambah atau membaca informasi yang terkait dengan setiap entri string.

   TMyObject(MO.Objects[25]).Cats := 17;
   D:=TMyObject(MO.Objects[25]).Dogs;

Ini berfungsi dengan baik selama objek tidak memiliki konstruktor. Jika objek memiliki konstruktor, konstruktornya tidak akan dipanggil saat objek dibuat karena konstruktor untuk TObject bukan virtual.

Adakah yang bisa memikirkan cara mengatasi masalah ini. Saya telah melihat solusi yang menggunakan pustaka RTTI, tetapi ini ada di Delphi-7, yang tidak memiliki pustaka RTTI.

Selain itu, tampaknya agak aneh bahwa konstruktor TObject bukanlah virtual. Jika ya, itu akan mengaktifkan semua jenis fitur yang berguna seperti yang saya coba terapkan.

EDIT: Saran Remy di bawah ini hanya dorongan yang saya butuhkan. Saya awalnya mencoba strategi serupa, tetapi saya tidak dapat membuatnya berhasil. Ketika tampaknya tidak berfungsi seperti yang saya pikir seharusnya, saya berasumsi pasti ada sesuatu yang saya tidak mengerti tentang metode virtual. Posnya mendorong saya untuk melihatnya lagi. Ternyata saya telah meninggalkan petunjuk "Ganti" untuk pembuat objek yang ingin saya lampirkan. Sekarang berfungsi sebagaimana mestinya.

Masalah lain yang saya khawatirkan adalah bahwa saya telah menggunakan Auto Strings di banyak aplikasi lain yang objeknya didasarkan pada "TObject" dan saya tidak ingin kembali dan mengubah semua kode itu. Saya memecahkan masalah itu dengan membebani konstruktor dan memilikinya untuk objek berbasis TObject dan objek TAutoClass saya yang lain:

  constructor Create(ObjClass: TAutoClass); overload; virtual;
  constructor Create(ObjClass: TClass); overload; virtual;

Bergantung pada konstruktor mana yang dipanggil, kelas objek disimpan dalam variabel yang berbeda dalam variabel yang berbeda.

 private
  AutoClass: TAutoClass;
  ObjectClass: TClass;

Kemudian ketika objek dibangun, saya memeriksa untuk melihat mana yang telah ditetapkan dan menggunakan yang itu:

procedure TAutoString.CreateClassInstance(Index: integer);
begin
   if AutoClass<>nil then Objects[Index]:=AutoClass.Create
   else Objects[Index]:=ObjectClass.Create
end;

Versi baru bekerja sempurna dengan kedua jenis objek.

Jawaban

3 RemyLebeau Sep 12 2020 at 03:54

Untuk melakukan apa yang Anda inginkan, Anda harus menentukan kelas dasar untuk objek daftar Anda berasal, dan kemudian Anda dapat menambahkan konstruktor virtual ke kelas itu. ObjectClassAnggota Anda harus menggunakan jenis kelas itu daripada menggunakan TClass.

Sebagai contoh:

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;

...

Kemudian, Anda cukup menyesuaikan kelas objek turunan Anda untuk digunakan TAutoStringObjectalih-alih TObject, misalnya:

type
  TMyObject = class(TAutoStringObject)
  public
    ...
    constructor Create; override;
  end;

MO := TAutoString.Create(TMyObject);
...

Dan konstruktornya akan dipanggil, seperti yang diharapkan.