为什么在VCL控件上调用TRttiContext.GetType时会重复某些属性?

问题描述

为什么在VCL控件上调用TRttiContext.GetType时,某些属性会重复(例如ActionAlign,而其他属性却没有(AlignWithMargins)重复出现?

uses
  System.RTTI,System.Generics.Collections,System.Generics.Defaults;

//....

procedure TForm11.btnShowPropertiesClick(Sender: TObject);
var
  R: TRttiContext;
  Props: TArray<TRttiProperty>;
  Prop : TRttiProperty;
begin
  memo1.Clear;
  R := TRttiContext.Create;
  Props := R.GetType(Sender.Classtype).GetProperties;

  //Sort properties by name
  TArray.sort<TRttiProperty>(props,TComparer<TRttiProperty>.Construct(
      function(const Left,Right: TRttiProperty): Integer
      begin
        result := CompareText(Left.Name,Right.Name);
      end
    )
  );

  for prop in Props do
  begin
    try
      Memo1.Lines.Add(
         Prop.Name + ' : ' +
         Prop.PropertyType.ToString + ' = ' +
         Prop.GetValue(Sender).ToString);
    except
      Memo1.Lines.Add(Prop.Name + ' generated an exception');
    end;
  end;
end;

输出

Action : TBasicAction = (empty)
Action : TBasicAction = (empty)
Align : TAlign = alNone
Align : TAlign = alNone
Aligndisabled : Boolean = False
AlignWithMargins : Boolean = False
Anchors : TAnchors = [akLeft,akTop]
Anchors : TAnchors = [akLeft,akTop]
BiDiMode : TBiDiMode = bdLeftToRight
BiDiMode : TBiDiMode = bdLeftToRight
...

解决方法

如果您将Prop.Parent.Name添加到填充备忘录的循环中,则可以很容易地找到原因:

for prop in Props do
begin
  try
    Memo1.Lines.Add(
       Prop.Parent.Name + '.' + { added }
       Prop.Name + ' : ' +
       Prop.PropertyType.ToString + ' = ' +
       Prop.GetValue(Sender).ToString);
  except
    Memo1.Lines.Add(Prop.Name + ' generated an exception');
  end;
end;

上面的代码产生:

TButton .Action:TBasicAction =(空)
TControl .Action:TBasicAction =(空)
TControl .Align:TAlign = alNone
TButton .Align:TAlign = alNone
TWinControl .AlignDisabled:布尔值= False
TControl .AlignWithMargins:布尔值= False
TControl 。锚点:锚点= [akLeft,akTop]
TButton 。锚点:锚点= [akLeft,akTop]
TButton .BiDiMode:TBiDiMode = bdLeftToRight
TControl .BiDiMode:TBiDiMode = bdLeftToRight
...

现在您可以清楚地看到GetProperties枚举了具有更高可见性或顺序更改的后代类中重新引入的属性。当TCustomMyControlSomeProperty可见性定义protected并在TMyControl级别重新引入published时,这是控件的典型用法。

您可以尝试在TButton声明之前为TForm11添加插入器类:

type
  TButton = class(Vcl.StdCtrls.TButton)
  published
    property AlignWithMargins;
  end;

输出将反映更改。我添加了属性声明类型的完全限定名称(Prop.Parent.QualifiedName,以使TButton明显来自我自己的单位。

Vcl.StdCtrls.TButton.Action:TBasicAction =(空)
Vcl.Controls.TControl.Action:TBasicAction =(空)
Vcl.Controls.TControl.Align:TAlign = alNone
Vcl.StdCtrls.TButton.Align:TAlign = alNone
Vcl.Controls.TWinControl.AlignDisabled:布尔值=假
Vcl.Controls.TControl.AlignWithMargins:布尔值= False
Unit1.TButton.AlignWithMargins:布尔值= False
Vcl.Controls.TControl.Anchors:TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.Anchors:TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.BiDiMode:TBiDiMode = bdLeftToRight
Vcl.Controls.TControl.BiDiMode:TBiDiMode = bdLeftToRight
...

这种行为不仅限于控件。可以在从祖先类重新引入属性的任何类中观察到它。

,

GetDeclaredProperties()会更有用吗?

help指出GetDeclaredProperties()和GetProperties()之间的区别是:

使用 GetDeclaredProperties 方法获取所有 以反射类型声明的属性。获取列表 反映类型中所有属性的值(包括继承的 ),请改用GetProperties方法。

因此,在您的情况下,我认为GetProperties()可以按设计工作,并且同时返回属性和继承的属性-因此为什么某些属性会出现多次。