使用RTTI获取/设置子属性

时间:2011-10-04 14:52:56

标签: delphi delphi-xe rtti

鉴于下面的代码片段,使用GetPropValue(MyComponent,'MySubComponent.Prop1')会引发EPropertyError异常。 如何使用GetPropValue / SetPropValue检索或设置SubProperties的值?

Type
  TMySubComponent = class(TInterfacedPersitent)
  private
    FProp1: Integer;
  published
    property Prop1: integer read FProp1 write FProp1;
  end;

  TMyComponent = class(TCompoent)
  private
    FMySubComponent : TMySubcomponent; 
  published
    property MySubComponent: TMySubComponent read FMySubComponent write FMySubComponent ;
  end;

2 个答案:

答案 0 :(得分:6)

正如Robert所说,不支持点符号,但您可以使用RTTI轻松创建设置或获取子属性值的函数。检查此样本

{$APPTYPE CONSOLE}

uses
  Rtti,
  Classes,
  SysUtils;


Type
  TMySubComponent = class(TInterfacedPersistent)
  private
    FProp1: Integer;
  published
    property Prop1: integer read FProp1 write FProp1;
  end;

  TMyComponent = class(TComponent)
  private
    FMySubComponent : TMySubcomponent;
  published
    property MySubComponent: TMySubComponent read FMySubComponent write FMySubComponent ;
  end;



procedure SetObjValueEx(const ObjPath:string;AInstance:TObject;AValue:TValue);
Var
 c            : TRttiContext;
 Prop         : string;
 SubProp      : string;
 pm           : TRttiProperty;
 p            : TRttiProperty;
 Obj          : TObject;
begin
 Prop:=Copy(ObjPath,1,Pos('.',ObjPath)-1);
 SubProp:=Copy(ObjPath,Pos('.',ObjPath)+1);
 c := TRttiContext.Create;
 try
   for pm in c.GetType(AInstance.ClassInfo).GetProperties do
   if CompareText(Prop,pm.Name)=0 then
   begin
     p := c.GetType(pm.PropertyType.Handle).GetProperty(SubProp);
      if Assigned(p) then
      begin
        Obj:=pm.GetValue(AInstance).AsObject;
        if Assigned(Obj) then
          p.SetValue(Obj,AValue);
      end;
      break;
   end;
 finally
   c.Free;
 end;
end;


function GetObjValueEx(const ObjPath:string;AInstance:TObject):TValue;
Var
 c            : TRttiContext;
 Prop         : string;
 SubProp      : string;
 pm           : TRttiProperty;
 p            : TRttiProperty;
 Obj          : TObject;
begin
 Prop:=Copy(ObjPath,1,Pos('.',ObjPath)-1);
 SubProp:=Copy(ObjPath,Pos('.',ObjPath)+1);
 c := TRttiContext.Create;
 try
   for pm in c.GetType(AInstance.ClassInfo).GetProperties do
   if CompareText(Prop,pm.Name)=0 then
   begin
     p := c.GetType(pm.PropertyType.Handle).GetProperty(SubProp);
      if Assigned(p) then
      begin
        Obj:=pm.GetValue(AInstance).AsObject;
        if Assigned(Obj) then
          Result:=p.GetValue(Obj);
      end;
      break;
   end;
 finally
   c.Free;
 end;
end;

Var
  MyComp : TMyComponent;
begin
  try
     MyComp:=TMyComponent.Create(nil);
     try
       MyComp.MySubComponent:=TMySubComponent.Create;
       //Set the value of the property 
       SetObjValueEx('MySubComponent.Prop1',MyComp,777);
       //Get the value of the property 
       Writeln(Format('The value of MySubComponent.Prop1 is %d',[GetObjValueEx('MySubComponent.Prop1',MyComp).AsInteger]));
     finally
       MyComp.Free;
     end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

答案 1 :(得分:4)

不支持您在问题中使用的点符号。

您需要获取SubComponent的值,然后对各个属性执行Set and Get。

var
  C: TRttiContext;
  MyComp : TMyComponent;
  MyCompType : TRttiInstanceType;
  MySubCompType : TRttiInstanceType;
  MySubComponentValue : TValue;
begin
  MyComp := TMyComponent.create(Self); 
  ...
  // RTTI.Pas Method
  MyCompType :=  c.GetType(TMyComponent.ClassInfo) as TRttiInstanceType;
  MySubCompType := c.GetType(TMySubComponent.ClassInfo) as TRttiInstanceType;
  MySubComponentValue := MyCompType.GetProperty('MySubComponent').GetValue(MyComp);

  if Not MySubComponentValue.IsEmpty then
  begin
      MySubCompType.GetProperty('Prop1').SetValue(MySubComponentValue.AsObject,43);
  end;

 //TypInfo.pas Method
 SubComp := GetObjectProp(MyComp,'MySubComponent');
 if Assigned(SubComp) then
 begin
    SetPropValue(SubComp,'Prop1',5);
    prop1Value := GetPropValue(SubComp,'Prop1');
 end;

end;

TypInfo.pas方法仅适用于已发布的属性,您可以使用RTTI.pas方法获取公共属性。