Delphi - Создать класс из строки - PullRequest
17 голосов
/ 24 апреля 2011

Я получил такой код

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

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

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

Ответы [ 3 ]

27 голосов
/ 25 апреля 2011

Начиная с 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, так как есть много доступных вариантов.


Обновлено На основании запроса Дэвида:

Сравнение использования конструкции с использованием Class Type (Virtual Constructor) с TRttiType.Invoke

Метод класса: (Виртуальный конструктор)

  • Работает во всех версиях Delphi
  • Производит более быстрый код
  • Требует знания предков во время компиляции.
  • Требуется реестр классов для поиска класса по строковому имени (например, как упомянуто RRUZ)

Метод TRttiType.Invoke ()

  • Работает только в Delphi 2010 или более поздней версии.
  • Код медленнее
  • Реализует реестр классов, который учитывает конфликты имен
  • Требуется NO знание происхождения во время компиляции.

Лично я нахожу, что каждый служит своей цели. Если я знаю все типы заранее, я использую метод класса типа.

16 голосов
/ 24 апреля 2011

Вы можете использовать функцию GetClass, но перед тем, как зарегистрировать классы, используйте методы RegisterClass или RegisterClasses.

GetClass(const AClassName: string): TPersistentClass;
10 голосов
/ 24 апреля 2011

Обычный способ сделать это с помощью виртуальных конструкторов.Хороший пример - TComponent, который вам, без сомнения, знаком.

TComponent имеет следующий конструктор:

constructor Create(AOwner: TComponent); virtual;

Другой ключ к этому - TComponentClass, который объявлен какclass of TComponent.

Когда VCL выполняет потоковую передачу файлов .dfm, он читает имя класса из файла .dfm и, с помощью какого-то процесса, который нам здесь не нужен, преобразует это имя в переменную.ComponentClass скажем, типа TComponentClass.Затем он может создать экземпляр объекта с помощью:

Component := ComponentClass.Create(Owner);

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

Если вам нужно использоватьstring, чтобы идентифицировать класс, тогда вам все равно нужно будет найти подпрограмму поиска для преобразования имени класса строки в ссылку на класс.Вы могли бы, если это удобно, подключиться к тому же механизму 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...