Использование RTTI для получения типа элементов TObjectList<T> в Delphi без создания экземпляров
Вопрос, поднятый в данном запросе, заключается в том, как получить тип элементов, используемых в TObjectList<T> в Delphi, используя только информацию RTTI, без создания экземпляров объектов. Это может быть необходимо, например, при реализации универсального кода для сохранения и загрузки объектов Delphi в поток, используя RTTI.
Проблема
При загрузке объекта из потока, на основе только известного типа класса объекта, который должен быть загружен, у нас не будет доступен экземпляр объекта до завершения процесса загрузки. В этом случае мы можем иметь доступ только к чистой RTTI-информации класса. Например, для класса TTestClass с полем test_list типа TList<string>, необходимо определить, что это поле является TList<T>, где T является string, чтобы понимать, какие данные ожидать из потока для подэлементов.
Решение
К сожалению, RTTI не генерируется для параметров Generic. Единственный способ обнаружить значение T в Generic контейнере, таком как TList<T>, - это получить TRttiType для целевого поля, вызвать его метод ToString() для получения имени класса как строки и разобрать подстроку между скобками. Например:
uses
..., System.StrUtils, System.Rtti;
var
Ctx: TRttiContext;
s: string;
OpenBracket, CloseBracket: Integer;
...
begin
...
s := Ctx.GetType(TTestClass).GetField('test_list').FieldType.ToString; // возвращает 'TList<System.string>'
OpenBracket := Pos('<', s);
CloseBracket := PosEx('>', s, OpenBracket+1);
s := Copy(s, OpenBracket+1, CloseBracket-OpenBracket-1); // возвращает 'System.string'
...
end;
После извлечения Generic параметра в виде строки, можно использовать TRttiContext.FindType(), если необходимо получить RTTI для этого типа.
Также стоит упомянуть, что в коде DSharp.Core.Reflection.pas определена вспомогательная структура TRttiTypeHelper, которая добавляет метод GetGenericArguments() к TRttiType, что позволяет извлечь информацию о Generic параметрах без необходимости вручную разбирать строку.
uses
..., System.Rtti, DSharp.Core.Reflection;
var
Ctx: TRttiContext;
arr: TArray<TRttiType>;
typ: TRttiType;
s: string;
...
begin
...
arr := Ctx.GetType(TTestClass).GetField('test_list').FieldType.GetGenericArguments;
typ := arr[0]; // возвращает RTTI для System.String
s := typ.ToString; // возвращает 'System.string'
...
end;
Комментарии
Следует отметить, что в RTTI для Generic параметров нет информации, но она доступна в RTTI для реализованных типов. Например, TList<String> (конкретная реализация TList<T>) имеет RTTI, в отличие от TList<T>, который сам по себе не является конкретным классом и не имеет собственной RTTI. Поэтому для получения информации о Generic параметрах приходится использовать ручное разбор строки, как описано выше.
Альтернативный ответ
Также был предложен альтернативный подход, который использует RTTI для получения класса типа элементов из списка (TList, TObjectList и т.д.). В примере функции TUtilRtti.GetSubTypeItemClassFromList возвращает класс типа, используемый в списке.
class function TUtilRtti.GetSubTypeItemClassFromList(ObjectList: TObject): TClass;
...
begin
...
ctxRtti := TRttiContext.Create;
typeRtti := ctxRtti.GetType( ObjectList.ClassType );
methodRtti := typeRtti.GetMethod('Add');
for parameterRtti in methodRtti.GetParameters do
begin
if SameText(parameterRtti.Name,'Value') then
begin
if parameterRtti.ParamType.IsInstance then
result := parameterRtti.ParamType.AsInstance.MetaclassType;
break;
end;
end;
ctxRtti.Free;
end;
Этот подход может быть полезен для определения типа элементов, которые могут быть добавлены в список, без создания экземпляров объектов.
Заключение
Для получения типа элементов TObjectList<T> в Delphi без создания экземпляров, можно использовать информацию, полученную с помощью RTTI, в том числе с помощью ручного разбора строки для извлечения информации о Generic параметрах. Это может быть особенно полезно при реализации универсальных механизмов сериализации и десериализации объектов.
Запрос касается использования RTTI для определения типа элементов коллекции `TObjectList` в Delphi без создания экземпляров объектов, что может быть необходимо для универсальной обработки данных, например, при их сохранении и загру
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.