Метод вызова Delphi на основе информации RTTI

Привет всем, сначала извините за мой плохой английский. Рассмотрим следующее (не фактический код):

IMyInterface = Interface(IInterfce)
  procedure Go();
end;

MyClass = class(IMyInterface)
  procedure Go();
end;

MyOtherClass = class
published
  property name: string;
  property data: MyClass;
end;

Я устанавливаю свойства «MyOtherClass», используя RTTI. Для строкового свойства это легко, но мой вопрос:

Как я могу получить ссылку на свойство «данные» (MyClass), чтобы я мог вызвать метод Go()?

Я хочу сделать что-то вроде этого (псевдокод):

for i:= 0 to class.Properties.Count  
  if (propertyType is IMyInterface) then
    IMyInterface(class.properties[i]).Go()

(если бы это был C# :()

PS: это в delphi 7 (знаю, даже хуже)


person Leo    schedule 08.09.2009    source источник
comment
RTTI значительно улучшен в Delphi 2010. Существует простой в использовании модуль под названием rtti.pas. другими словами, переключитесь на d2010, и вы получите меньше боли.   -  person Bernd Ott    schedule 09.09.2009
comment
Я бы с удовольствием это сделал, в Delphi есть много интересных новых функций... но в этом проекте я действительно не могу :/ - Но я постараюсь получить пробную версию 2010, просто чтобы увидеть, как это работает, и убедить мой босс :D   -  person Leo    schedule 09.09.2009
comment
В настоящее время embarcadero находится на гастролях в Германии. devtracks.de   -  person Bernd Ott    schedule 09.09.2009


Ответы (2)


Если строковое свойство легко, как вы говорите, то я предполагаю, что вы вызываете GetStrProp и SetStrProp из модуля TypInfo. Свойства типа класса могут быть одинаково просты с GetObjectProp и SetObjectProp.

if Supports(GetObjectProp(Obj, 'data'), IMyInterface, Intf) then
  Intf.Go;

Если вам не особо нужен интерфейс, и вы знаете, что свойство data имеет тип TMyClass, то можно пойти более прямым путем:

(GetObjectProp(Obj, 'data') as TMyClass).Go;

Это требует, чтобы свойство имело ненулевое значение.

Если вы не знаете имя нужного вам свойства, вы можете использовать некоторые другие вещи в TypInfo для его поиска. Например, вот функция, которая найдет все опубликованные свойства объекта, имеющие значения, реализующие IMyInterface; он вызывает Go для каждого из них в произвольном порядке.

procedure GoAllProperties(Other: TObject);
var
  Properties: PPropList;
  nProperties: Integer;
  Info: PPropInfo;
  Obj: TObject;
  Intf: IMyInterface;
  Unk: IUnknown;
begin
  // Get a list of all the object's published properties
  nProperties := GetPropList(Other.ClassInfo, Properties);
  if nProperties > 0 then try
    // Optional: sort the list
    SortPropList(Properties, nProperties);

    for i := 0 to Pred(nProperties) do begin
      Info := Properties^[i];
      // Skip write-only properties
      if not Assigned(Info.GetProc) then
        continue;

      // Check what type the property holds
      case Info.PropType^^.Kind of
        tkClass: begin
          // Get the object reference from the property
          Obj := GetObjectProp(Other, Info);
          // Check whether it implements IMyInterface
          if Supports(Obj, IMyInterface, Intf) then
            Intf.Go;
        end;

        tkInterface: begin
          // Get the interface reference from the property
          Unk := GetInterfaceProp(Obj, Info);
          // Check whether it implements IMyInterface
          if Supports(Unk, IMyInterface, Intf) then
            Intf.Go;
        end;
      end;
    end;
  finally
    FreeMem(Properties);
  end;
end;
person Rob Kennedy    schedule 09.09.2009
comment
Спасибо, Роб, ваше решение отлично решило мою проблему. Это было именно то, что я искал - вы заслуживаете значок RTTI-Hero :) - person Leo; 09.09.2009

Вы можете получить массив всех опубликованных свойств, вызвав GetPropInfos(MyClass.ClassInfo). Это массив указателей PPropInfo. И вы можете получить специфические для типа данные из PPropInfo, вызвав для него GetTypeData, который возвращает PTypeData. Запись, на которую он указывает, будет содержать искомую информацию о ссылке на класс.

person Mason Wheeler    schedule 08.09.2009