为什么在 VCL 控件上调用 TRttiContext.GetType 时某些属性会重复?
Why are some properties repeated when TRttiContext.GetType is called on a VCL Control?
为什么在 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 = (empty)
TControl.Action : TBasicAction = (empty)
TControl.Align : TAlign = alNone
TButton.Align : TAlign = alNone
TWinControl.AlignDisabled : Boolean = False
TControl.AlignWithMargins : Boolean = False
TControl.Anchors : TAnchors = [akLeft,akTop]
TButton.Anchors : TAnchors = [akLeft,akTop]
TButton.BiDiMode : TBiDiMode = bdLeftToRight
TControl.BiDiMode : TBiDiMode = bdLeftToRight
...
现在您可以清楚地看到 GetProperties
枚举了在后代 class 中重新引入的具有更高可见性或更改顺序的属性。当 TCustomMyControl
定义具有 protected
可见性的 SomeProperty
并且 TMyControl
在 published
级别重新引入它时,这是典型的控件。
您可以尝试在 TForm11
声明之前为 TButton
添加插入器 class:
type
TButton = class(Vcl.StdCtrls.TButton)
published
property AlignWithMargins;
end;
输出将反映更改。我添加了 属性 的声明类型 (Prop.Parent.QualifiedName
) 的完全限定名称,以便更明显地表明 TButton
来自我自己的单位。
Vcl.StdCtrls.TButton.Action : TBasicAction = (empty)
Vcl.Controls.TControl.Action : TBasicAction = (empty)
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.Anchors : TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.BiDiMode : TBiDiMode = bdLeftToRight
Vcl.Controls.TControl.BiDiMode : TBiDiMode = bdLeftToRight
...
这种行为不仅限于控件。可以在任何 class 上观察到从祖先 class.
重新引入属性
GetDeclaredProperties() 会更有用吗?
help 指出 GetDeclaredProperties() 和 GetProperties() 之间的区别是:
Use the GetDeclaredProperties method to obtain a list of all the
properties that are declared in the reflected type. To obtain the list
of all properties in the reflected type (including the inherited
ones), use the GetProperties method instead.
因此我认为在您的情况下 GetProperties() 正在按设计工作,并返回属性和继承的属性 - 因此某些属性出现不止一次。
为什么在 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 = (empty)
TControl.Action : TBasicAction = (empty)
TControl.Align : TAlign = alNone
TButton.Align : TAlign = alNone
TWinControl.AlignDisabled : Boolean = False
TControl.AlignWithMargins : Boolean = False
TControl.Anchors : TAnchors = [akLeft,akTop]
TButton.Anchors : TAnchors = [akLeft,akTop]
TButton.BiDiMode : TBiDiMode = bdLeftToRight
TControl.BiDiMode : TBiDiMode = bdLeftToRight
...
现在您可以清楚地看到 GetProperties
枚举了在后代 class 中重新引入的具有更高可见性或更改顺序的属性。当 TCustomMyControl
定义具有 protected
可见性的 SomeProperty
并且 TMyControl
在 published
级别重新引入它时,这是典型的控件。
您可以尝试在 TForm11
声明之前为 TButton
添加插入器 class:
type
TButton = class(Vcl.StdCtrls.TButton)
published
property AlignWithMargins;
end;
输出将反映更改。我添加了 属性 的声明类型 (Prop.Parent.QualifiedName
) 的完全限定名称,以便更明显地表明 TButton
来自我自己的单位。
Vcl.StdCtrls.TButton.Action : TBasicAction = (empty)
Vcl.Controls.TControl.Action : TBasicAction = (empty)
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.Anchors : TAnchors = [akLeft,akTop]
Vcl.StdCtrls.TButton.BiDiMode : TBiDiMode = bdLeftToRight
Vcl.Controls.TControl.BiDiMode : TBiDiMode = bdLeftToRight
...
这种行为不仅限于控件。可以在任何 class 上观察到从祖先 class.
重新引入属性GetDeclaredProperties() 会更有用吗?
help 指出 GetDeclaredProperties() 和 GetProperties() 之间的区别是:
Use the GetDeclaredProperties method to obtain a list of all the properties that are declared in the reflected type. To obtain the list of all properties in the reflected type (including the inherited ones), use the GetProperties method instead.
因此我认为在您的情况下 GetProperties() 正在按设计工作,并返回属性和继承的属性 - 因此某些属性出现不止一次。