РТТИ:Могу ли я получить Тип по имени?
-
13-09-2019 - |
Вопрос
Учитывая текстовую строку, содержащую имя типа, есть ли какой-нибудь способ получить сам соответствующий тип?
Я хочу сделать что-то вроде этого:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
Я просмотрел несколько объяснений RTTI онлайн и просмотрел модули Delphi и не вижу того, что я ищу.Возможно ли это?
Решение
Нет, дженерики полностью рассчитаны на компиляцию.
Другие советы
Новый модуль RTTI в Delphi 2010 имеет способ извлечения типов, объявленных в разделе интерфейса units.Для любого заданного типа, представленного TRttiType
например, в TRttiType.QualifiedName
свойство возвращает имя , которое может быть использовано с TRttiContext.FindType
позже, чтобы получить тип.Квалифицируемое имя - это полное имя модуля (включая пространства имен, если они существуют), за которым следует '.', за которым следует полное имя типа (включая внешние типы, если они вложенные).
Таким образом, вы могли бы получить представление целочисленного типа (в виде TRttiType
) с context.FindType('System.Integer')
.
Но этот механизм не может быть использован для извлечения экземпляров универсальных типов, которые не были созданы во время компиляции;создание экземпляра во время выполнения требует генерации кода во время выполнения.
Вы всегда можете зарегистрировать свои типы в каком-нибудь реестре (управляемом списком строк или словарем) и создать заводскую функцию, которая затем вернет соответствующий объект.К сожалению, вам пришлось бы заранее знать, какие типы вам понадобятся.Что-то похожее на функции Delphi RegisterClass и FindClass (в блоке classes).Мое мышление состоит в том, чтобы поместить общий тип шаблона непосредственно в список.
Пример возможного использования:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
Редактировать: Вот конкретная простая реализация с использованием TDictionary из Generics.Коллекции для обработки хранилища реестра...Я оставлю извлечение этого в полезные методы в качестве простого упражнения для читателя.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
Еще одна ПРАВКА: Вчера вечером я немного подумал над этим и обнаружил еще одну технику, которую вы можете объединить с этой концепцией.Интерфейсы.Вот краткий пример бездействия, но его можно легко расширить:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
конечно, вам пришлось бы реализовать методы QueryInterface, _AddRef и _Release и расширить интерфейс, чтобы делать что-то более полезное.
Если вы забыли обобщения и базовые типы, функция "RegisterClass" была бы полезна.Но это не работает для дженериков или базовых типов.