I'm refactoring a component code, and I found the follow code:
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;
I'ts working perfectly today, but i want to use for/in in this method, something like this:
procedure TMenuToolbarButton.ClearActivation;
var
MyMenuToolbarButton: TMenuToolbarButton;
begin
for MyMenuToolbarButton in Self.Parent do
begin
MyMenuToolbarButton.FActivatedImage.Visible := False;
MyMenuToolbarButton.FActivatedImageGrowLeft.Visible := False;
end;
end;
I already tried with Generics.Collections casting the Self.Parent like this: TObjectList<TMenuToolbarButton>(Self.Parent)
So, I want to know if is there a better way to make the working code more "elegant"
The Parent is a TWinControl, not a TObjectList, so your attempted typecast is invalid.
You can't use a for.. in loop with the Components property directly, as it is not an iterable container that meets any of the documented requirements:
Delphi supports
for-element-in-collectionstyle iteration over containers. The following container iteration patterns are recognized by the compiler:
for Element in ArrayExpr do Stmt;
for Element in StringExpr do Stmt;
for Element in SetExpr do Stmt;
for Element in CollectionExpr do Stmt;
for Element in Record do Stmt;
The Components property is not an Array, a String, a Set, a Collection, or a Record, so it can't be iterated by a for..in loop.
However, TComponent itself satisfies the documented requirements of an iterable Collection:
To use the
for-inloop construct on a class or interface, the class or interface must implement a prescribed collection pattern. A type that implements the collection pattern must have the following attributes:
The class or interface must contain a public instance method called
GetEnumerator(). TheGetEnumerator()method must return a class, interface, or record type.The class, interface, or record returned by
GetEnumerator()must contain a public instance method calledMoveNext(). TheMoveNext()method must return aBoolean. Thefor-inloop calls this method first to ensure that the container is not empty.The class, interface, or record returned by
GetEnumerator()must contain a public instance, read-only property calledCurrent. The type of theCurrentproperty must be the type contained in the collection.
TComponent has a public GetEnumerator() method which returns a TComponentEnumerator object that internally iterates the Components property. But, since the property deals with TComponent objects, you will still have to manually typecast them inside the loop.
Try this:
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;
So, using a for..in loop does not really gain you anything useful over a traditional for..to loop in this situation.
TComponent implements method GetEnumerator by returning instance of TComponentEnumerator, which enumerates all components owned by this component. In order to use this enumerator you could change your local variable declaration to var MyMenuToolbarButton: TComponent;, but you would still need to type-cast inside the loop.
If you really, really want to use for..in loop for enumerating components of specified type, you can write your own generic enumerator:
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;
Usage:
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;
Few notes:
T or its descendants.for..to loop.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With