Подтвердить что ты не робот

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

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

В этом примере кода конструктор 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;
4b9b3361

Ответ 1

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

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;

В качестве альтернативы вы можете использовать type-casts для TMyClass (вместо класса TMyClass).

Ответ 2

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

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

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

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 part) в Rob Love Tech Corner.

Ответ 3

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

Ответ 4

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

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;

Ответ 5

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