我正在重构组件代码,发现以下代码:
procedure TMenuToolbarButton.ClearActivation;
var
i: Integer;
begin
for i := 0 to Self.Parent.ComponentCount -1 do
begin
if (Self.Parent.Components[i] is TMenuToolbarButton) then
begin
(Self.Parent.Components[i] as TMenuToolbarButton).FActivatedImage.Visible := False;
(Self.Parent.Components[i] as TMenuToolbarButton).FActivatedImageGrowLeft.Visible := False;
end;
end;
end;
我今天不能很好地工作,但是我想在此方法中使用for / in,例如:
procedure TMenuToolbarButton.ClearActivation;
var
MyMenuToolbarButton: TMenuToolbarButton;
begin
for MyMenuToolbarButton in Self.Parent do
begin
MyMenuToolbarButton.FActivatedImage.Visible := False;
MyMenuToolbarButton.FActivatedImageGrowLeft.Visible := False;
end;
end;
我已经尝试使用Generics.Collections像这样Self.Parent
投射TObjectList<TMenuToolbarButton>(Self.Parent)
所以,我想知道是否有更好的方法使工作代码更“优雅”
答案 0 :(得分:6)
Parent
是TWinControl
,而不是TObjectList
,因此您尝试的类型转换无效。
您不能直接将for.. in
循环与Components
属性一起使用,因为它不是一个满足任何documented requirements的可迭代容器:
Delphi在容器上支持
for-element-in-collection
样式的迭代。 编译器可以识别以下容器迭代模式:
对于ArrayExpr do Element中的元素;
对于StringExpr do Stmt中的元素;
对于SetExpr do Stmt中的元素;
对于CollectionExpr do Stmt中的元素;
对于“记录中的元素”,
Components
属性不是数组,字符串,集合,集合或记录,因此不能通过for..in
循环进行迭代。
但是,TComponent
本身满足了可迭代集合的文档要求:
要在类或接口上使用
for-in
循环构造,该类或接口必须实现规定的收集模式。实现收集模式的类型必须具有以下属性:
类或接口必须包含名为
GetEnumerator()
的公共实例方法。GetEnumerator()
方法必须返回类,接口或记录类型。
GetEnumerator()
返回的类,接口或记录必须包含称为MoveNext()
的公共实例方法。MoveNext()
方法必须返回一个Boolean
。for-in
循环首先调用此方法,以确保容器不为空。
GetEnumerator()
返回的类,接口或记录必须包含一个公共实例,名为Current
的只读属性。Current
属性的类型必须是集合中包含的类型。
TComponent
有一个公用的GetEnumerator()
方法,该方法返回一个TComponentEnumerator
对象,该对象在内部迭代Components
属性。但是,由于该属性处理TComponent
对象,因此您仍然必须在循环内手动进行类型转换。
尝试一下:
procedure TMenuToolbarButton.ClearActivation;
var
//i: Integer;
Comp: TComponent;
Btn: TMenuToolbarButton;
begin
//for i := 0 to Self.Parent.ComponentCount -1 do
for Comp in Self.Parent do
begin
//Comp := Self.Parent.Components[i];
if Comp is TMenuToolbarButton then
begin
Btn := TMenuToolbarButton(Comp);
Btn.FActivatedImage.Visible := False;
Btn.FActivatedImageGrowLeft.Visible := False;
end;
end;
end;
因此,在这种情况下,使用for..in
循环并不能真正获得比传统for..to
循环有用的任何东西。
答案 1 :(得分:4)
TComponent
通过返回TComponentEnumerator
的实例来实现方法GetEnumerator
,该实例枚举该组件拥有的所有组件。为了使用此枚举器,您可以将局部变量声明更改为var MyMenuToolbarButton: TComponent;
,但仍需要在循环内部进行类型转换。
如果您确实要使用for..in
循环枚举指定类型的组件,则可以编写自己的通用枚举器:
type
TComponentEnumerator<T: TComponent> = record
private
FIndex: Integer;
FComponent: TComponent;
public
constructor Create(AComponent: TComponent);
function GetCurrent: T; inline;
function GetEnumerator: TComponentEnumerator<T>;
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
constructor TComponentEnumerator<T>.Create(AComponent: TComponent);
begin
FIndex := -1;
FComponent := AComponent;
end;
function TComponentEnumerator<T>.GetCurrent: T;
begin
Result := T(FComponent.Components[FIndex]);
end;
function TComponentEnumerator<T>.GetEnumerator: TComponentEnumerator<T>;
begin
Result := Self;
end;
function TComponentEnumerator<T>.MoveNext: Boolean;
begin
Inc(FIndex);
while (FIndex < FComponent.ComponentCount) and (not (FComponent.Components[FIndex] is T)) do
Inc(FIndex);
Result := FIndex < FComponent.ComponentCount;
end;
用法:
procedure TMenuToolbarButton.ClearActivation;
var
MyMenuToolbarButton: TMenuToolbarButton;
begin
for MyMenuToolbarButton in TComponentEnumerator<TMenuToolbarButton>.Create(Self.Parent) do
begin
MyMenuToolbarButton.FActivatedImage.Visible := False;
MyMenuToolbarButton.FActivatedImageGrowLeft.Visible := False;
end;
end;
一些注意事项:
T
的组件或其后代。for..to
循环相比,使用枚举器会添加small overhead。