Delphi - создать класс из строки
Я получил код вроде этого
name := 'Foo';
If name = 'Foo' then
result := TFoo.Create
else if name = 'Bar' then
result := TBar.Create
else if name = 'FooFoo' then
result := TFooFoo.Create;
Есть ли способ сделать только
result := $name.create
или какой-либо способ создания класса на основе значения переменной?
Все классы расширили один и тот же базовый класс.
Ответы
Ответ 1
Начиная с Delphi 2010, расширенный RTTI позволяет вам делать это, не создавая собственный реестр классов.
С помощью блока RTTI
у вас есть несколько доступных опций.
Для конструкторов с минимальным значением параметра один из самых простых.
var
C : TRttiContext;
O : TObject;
begin
O := (C.FindType('UnitName.TClassName') as TRttiInstanceType).MetaClassType.Create;
...
end;
Ниже приведен пример передачи параметра с помощью TRttiMethod.Invoke()
var
C : TRttiContext;
T : TRttiInstanceType;
V : TValue;
begin
T := (C.FindType('StdCtrls.TButton') as TRttiInstanceType);
V := T.GetMethod('Create').Invoke(T.metaClassType,[self]);
(V.AsObject as TWinControl).Parent := self;
end;
Я написал несколько статей в блоке RTTI
, поскольку есть много доступных опций.
Обновлено Основано на запросе Дэвида:
Сравнение использования конструкции с использованием типа класса (Virtual Constructor) с TRttiType.Invoke
Метод типа класса: (Виртуальный конструктор)
- Работает во всех версиях Delphi
- Производит более быстрый код
- Требуется знание родословной во время компиляции.
- Требуется реестр классов для поиска класса по имени строки (например, указанному RRUZ)
Метод TRttiType.Invoke()
- Работает только в Delphi 2010 или новее.
- Более медленный код
- Реализует реестр классов, учитывающий конфликты имен
- Требуется НЕТ знание предка во время компиляции.
Я лично считаю, что каждая из них служит другой цели. Если я знаю все типы вверх, я использую метод типа класса.
Ответ 2
Вы можете использовать функцию GetClass
, но прежде чем вы должны зарегистрировать классы, используя RegisterClass
или RegisterClasses
.
GetClass(const AClassName: string): TPersistentClass;
Ответ 3
Обычный способ сделать это - с виртуальными конструкторами. Хорошим примером является TComponent
, с которым вы, несомненно, знакомы.
TComponent
имеет следующий конструктор:
constructor Create(AOwner: TComponent); virtual;
Другой ключ к этому - TComponentClass
, который объявлен как class of TComponent
.
Когда VCL передает файлы .dfm, он считывает имя класса из .dfm файла и, в результате какого-то процесса, который нам не нужно покрывать здесь, преобразует это имя в переменную, ComponentClass
говорит о типе TComponentClass
. Затем он может создавать объект с помощью:
Component := ComponentClass.Create(Owner);
Это большое преимущество наличия виртуального конструктора, и я бы посоветовал вам принять тот же подход.
Если вам нужно использовать строку для идентификации класса, вам все равно придется придумать подпрограмму lookup для преобразования из имени класса строки в ссылку класса. Вы можете, если удобно, подключиться к тому же механизму VCL, который использует TComponent
, а именно RegisterClass
.
Альтернативно, если вы могли бы заменить name
в своем коде ссылкой на класс, вы могли бы написать:
type
TFoo = class
constructor Create; virtual;
end;
TBar = class(TFoo);
TFooClass = class of TFoo;
var
MyClass: TFooClass;
...
MyClass := TFoo;
result := MyClass.Create;//creates a TFoo;
MyClass := TBar;
result := MyClass.Create;//creates a TBar;