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

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

или какой-либо способ создания класса на основе значения переменной?

Все классы расширили один и тот же базовый класс.

4b9b3361

Ответ 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;