Как я могу создать объект Delphi из ссылки на класс и обеспечить выполнение конструктора? - PullRequest
20 голосов
/ 26 апреля 2009

Как я могу создать экземпляр объекта, используя ссылку на класс, и обеспечить выполнение конструктора?

В этом примере кода конструктор TMyClass не будет вызываться:

type
   TMyClass = class(TObject)
     MyStrings: TStrings;
     constructor Create; virtual;
   end;

constructor TMyClass.Create;
begin
   MyStrings := TStringList.Create;
end;

procedure Test;
var
   Clazz: TClass;
   Instance: TObject;
begin
   Clazz := TMyClass;
   Instance := Clazz.Create;
end;

Ответы [ 5 ]

26 голосов
/ 26 апреля 2009

Используйте это:

type
  TMyClass = class(TObject)
    MyStrings: TStrings;
    constructor Create; virtual;
  end;
  TMyClassClass = class of TMyClass; // <- add this definition

constructor TMyClass.Create;
begin
   MyStrings := TStringList.Create;
end;

procedure Test;
var
  Clazz: TMyClassClass; // <- change TClass to TMyClassClass
  Instance: TObject;
begin
   Clazz := TMyClass; // <- you can use TMyClass or any of its child classes. 
   Instance := Clazz.Create; // <- virtual constructor will be used
end;

В качестве альтернативы, вы можете использовать приведение типов к TMyClass (вместо «класса TMyClass»).

22 голосов
/ 18 сентября 2010

Решение Александра хорошее, но в определенных ситуациях его недостаточно. Предположим, вы хотите установить класс TClassFactory, в котором ссылки TClass могут храниться во время выполнения и произвольное количество экземпляров, полученных позже.

Такая фабрика классов никогда не узнает ничего о фактических типах классов, которые она содержит, и, следовательно, не может привести их к соответствующим мета-классам. Для вызова правильных конструкторов в таких случаях будет работать следующий подход.

Во-первых, нам нужен простой демонстрационный класс (не обращайте внимания на публичные поля, это только для демонстрационных целей).

interface

uses
  RTTI;

type
  THuman = class(TObject)
  public
    Name: string;
    Age: Integer;

    constructor Create(); virtual;
  end;

implementation

constructor THuman.Create();
begin
  Name:= 'John Doe';
  Age:= -1;
end;

Теперь мы создаем объект типа THuman исключительно с помощью RTTI и с правильным вызовом конструктора.

procedure CreateInstance();
var
  someclass: TClass;
  c: TRttiContext;
  t: TRttiType;
  v: TValue;
  human1, human2, human3: THuman;
begin
  someclass:= THuman;

  // Invoke RTTI
  c:= TRttiContext.Create;
  t:= c.GetType(someclass);

  // Variant 1a - instantiates a THuman object but calls constructor of TObject
  human1:= t.AsInstance.MetaclassType.Create;

  // Variant 1b - same result as 1a
  human2:= THuman(someclass.Create);

  // Variant 2 - works fine
  v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]);
  human3:= THuman(v.AsObject);

  // free RttiContext record (see text below) and the rest
  c.Free;

  human1.Destroy;
  human2.Destroy;
  human3.Destroy;
end;

Вы обнаружите, что объекты "human1" и "human2" были инициализированы нулем, то есть Name = '' и Age = 0, что не является тем, что мы хотим. Вместо этого объект human3 содержит значения по умолчанию, предоставленные в конструкторе THuman.

Обратите внимание, однако, что этот метод требует, чтобы ваши классы имели методы конструктора без параметров. Все вышеизложенное не было задумано мной, но объяснено блестяще и более подробно (например, часть c.Free) в Техническом уголке Роба Лава .

11 голосов
/ 27 апреля 2009

Пожалуйста, проверьте, является ли переопределение AfterConstruction опцией.

6 голосов
/ 26 апреля 2009

Ваш код немного изменен:

type
  TMyObject = class(TObject)
    MyStrings: TStrings;
    constructor Create; virtual;
  end;
  TMyClass = class of TMyObject;

constructor TMyObject.Create;
begin
  inherited Create;
  MyStrings := TStringList.Create;
end;

procedure Test; 
var
  C: TMyClass;
  Instance: TObject;
begin
   C := TMyObject;
   Instance := C.Create;
end;
0 голосов
/ 06 ноября 2014

Вы можете создать абстрактный метод в базовом классе, вызвать его в конструкторе и переопределить в дочерних классах, которые будут выполняться при создании из ссылки на класс.

...