问题描述
为什么在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;
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
枚举了具有更高可见性或顺序更改的后代类中重新引入的属性。当TCustomMyControl
用SomeProperty
可见性定义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()可以按设计工作,并且同时返回属性和继承的属性-因此为什么某些属性会出现多次。