В Delphi, при использовании RTTI (Run-Time Type Information), часто возникает задача определения класса, в котором фактически объявлен метод, особенно при работе с иерархией классов и наследованием. Предположим, у нас есть иерархия классов, где метод определен в базовом классе, но доступен и в производном. Как, имея TRttiMethod для этого метода, узнать, в каком классе он был изначально объявлен?
Рассмотрим пример, аналогичный приведенному в контексте:
type
TBase = class
public
class procedure Write;
end;
THigher = class(TBase);
class procedure TBase.Write;
begin
Writeln('TBase.Write');
end;
var
Context: TRttiContext;
T: TRttiType;
Method: TRttiMethod;
Item: TRttiMethod;
begin
Context := TRttiContext.Create;
T := Context.GetType(THigher);
Method := nil;
for Item in T.GetMethods('Write') do
if (Item.MethodKind = TMethodKind.mkClassProcedure) then
begin
Method := Item;
// Как получить TBase из Method ?
break;
end;
// ...
end;
В этом примере метод Write объявлен в TBase, но доступен через THigher. Задача состоит в том, чтобы, получив TRttiMethod для Write из THigher, определить, что он объявлен в TBase.
Решение:
Как правильно указано в контексте, свойство TRttiMethod.Parent содержит TRttiType, представляющий класс, в котором метод был объявлен.
Таким образом, для получения имени класса-владельца можно использовать:
if Assigned(Method) then
begin
Writeln('Класс-владелец метода: ' + Method.Parent.Name); // Выведет 'TBase'
end;
Если же требуется получить непосредственно TClass для класса-владельца, можно воспользоваться свойством AsInstance.MetaclassType:
if Assigned(Method) then
begin
var DeclaringClass: TClass := Method.Parent.AsInstance.MetaclassType;
Writeln('Класс-владелец метода (TClass): ' + DeclaringClass.ClassName); // Выведет 'TBase'
end;
Альтернативное решение (как упоминалось в контексте):
Можно использовать метод GetDeclaredMethods вместо GetMethods. GetDeclaredMethods возвращает только те методы, которые непосредственно объявлены в данном классе. Если метод не найден, можно рекурсивно пройтись по родительским классам, используя свойство ClassParent(TClass).
Пример:
function FindDeclaringClass(AClass: TClass; MethodName: string): TClass;
var
Context: TRttiContext;
RttiType: TRttiType;
Item: TRttiMethod;
begin
Result := nil;
Context := TRttiContext.Create;
RttiType := Context.GetType(AClass);
for Item in RttiType.GetDeclaredMethods(MethodName) do
begin
if (Item.MethodKind = TMethodKind.mkClassProcedure) then
begin
Result := AClass;
Exit;
end;
end;
if Assigned(AClass.ClassParent) then
begin
Result := FindDeclaringClass(AClass.ClassParent, MethodName);
end;
end;
var
DeclaringClass: TClass;
begin
DeclaringClass := FindDeclaringClass(THigher, 'Write');
if Assigned(DeclaringClass) then
begin
Writeln('Класс-владелец метода (TClass): ' + DeclaringClass.ClassName); // Выведет 'TBase'
end;
end;
Сравнение решений:
Использование TRttiMethod.Parent является более прямым и элегантным решением, так как оно сразу предоставляет необходимую информацию.
Использование GetDeclaredMethods и рекурсивного поиска по родительским классам может быть полезно, если требуется более детальный контроль над процессом поиска или если необходимо выполнить дополнительные проверки в процессе.
В большинстве случаев, использование TRttiMethod.Parent является предпочтительным решением для определения класса-владельца метода TRttiMethod. Оно проще в реализации и обеспечивает необходимую информацию без дополнительных затрат.
Контекст описывает, как определить класс, в котором был объявлен метод, полученный через RTTI, в иерархии классов Delphi.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.