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

Ali*_*ter 6 delphi rtti delphi-10.4-sydney

当在 VCL 控件上调用 TRttiContext.GetType 时,为什么某些属性会重复(例如Action和Align),而其他属性则不会 ( 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;
Run Code Online (Sandbox Code Playgroud)

输出

动作 : TBasicAction = (空)
动作 : TBasicAction = (空)
对齐:TAlign = alNone
对齐:TAlign = alNone
AlignDisabled : Boolean = False
AlignWithMargins : Boolean = False
锚点:TAnchors = [akLeft,akTop]
锚点:TAnchors = [akLeft,akTop]
BiDiMode : TBiDiMode = bdLeftToRight
BiDiMode : TBiDiMode = bdLeftToRight
...

Pet*_*olf 9

如果您将添加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;
Run Code Online (Sandbox Code Playgroud)

上面的代码产生:

TButton的.Action:TBasicAction =(空)
的TControl .Action:TBasicAction =(空)
的TControl .align伪:TAlign = alNone
TButton的.align伪:TAlign = alNone
TWinControl .AlignDisabled:布尔=假
的TControl .AlignWithMargins:布尔=假
的TControl .Anchors: TAnchors = [akLeft,akTop]
TButton的.Anchors:TAnchors = [akLeft,akTop]
TButton的.BiDiMode:TBiDiMode = bdLeftToRight
的TControl .BiDiMode:TBiDiMode = bdLeftToRight
...

现在您可以清楚地看到,它GetProperties枚举了在具有更高可见性或更改顺序的后代类中重新引入的属性。当以可见性TCustomMyControl定义并在级别重新引入它时,这对于控件来说是典型的。SomePropertyprotectedTMyControlpublished

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

type
  TButton = class(Vcl.StdCtrls.TButton)
  published
    property AlignWithMargins;
  end;
Run Code Online (Sandbox Code Playgroud)

输出将反映变化。我添加了属性声明类型的完全限定名称 ( 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 : Boolean = False
Vcl.Controls.TControl.AlignWithMargins : Boolean = False
Unit1.TButton.AlignWithMargins : Boolean = False
Vcl.Controls.TControl.Anchors : TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.锚点:TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.BiDiMode:TBiDiMode = bdLeftToRight
Vcl.Controls.TControl.BiDiMode:TBiDiMode = bdLeftToRight
...

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


小智 5

GetDeclaredProperties() 会更有用吗?

在帮助指出,()GetDeclaredProperties()和GetProperties中之间的区别是:

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

因此,我认为在您的情况下 GetProperties() 正在按设计工作,并返回属性和继承的属性 - 这就是为什么某些属性出现不止一次的原因。