Как я могу создать объект 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;
Ответы
Ответ 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
Вы можете создать абстрактный метод в базовом классе и вызвать его в конструкторе и переопределить в дочерних классах, которые будут выполняться при создании из ссылки на класс.