The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

Delphi – more for … in support for TComponent.Components using a class helper

Posted by jpluimers on 2009/05/12

With my blog posting last week on Delphi – class helper to add for … in support for TComponent.Components/ComponentCount  I ‘lied’ a bit. 

What I actually wanted to do was write code like this:

  for Component in Self.ComponentsOfType(TSQLQuery) do
    (Component as TSQLQuery).SQL[1] := FTableName;

This filters the enumeration to only enumerate components that are of type TSQLQuery or descend from type TSQLQuery.

So I wanted to write a ‘richer’ enumerator than just iterating over all components.
The scond part of the lie is that such an enumerator in fact already exists: there is already a TComponent.GetEnumerator method.
Primoz Gabrijelcic’s – web/blog – rightly noted in the comment where he pointed to a similar blog post he wrote for TControls.

Since I wrote this post last week as well, but scheduled to be published right before DelphiLive!, I just added a reference to his comment, site, blog and posting.

TComponent already has a GetEnumerator since Delphi 2006.
But my TComponentEnumerator was meant as a base class to show you how to descend from it.
And that’s what this blog post is about.
 

First the helper:

type
  TComponentHelper = class helper for TComponent
  public
    function ComponentsOfType(const ComponentClass: TComponentClass): TComponentOfTypeEnumeratorFactory;
  end;
//...
function TComponentHelper.ComponentsOfType(const ComponentClass: TComponentClass): TComponentOfTypeEnumeratorFactory;
begin
  Result := TComponentOfTypeEnumeratorFactory.Create(Self, ComponentClass);
end;

This time we are not providing the GetEnumerator ourselves, but a factory that provides the GetEnumerator.
So lets look at the factory a bit closer:

type
  TComponentOfTypeEnumeratorFactory = record
  strict private
    FComponent: TComponent;
    FComponentClass: TComponentClass;
  public
    constructor Create(const Component: TComponent; const ComponentClass: TComponentClass);
    function GetEnumerator: TComponentOfTypeEnumerator;
  end;
//...
procedure AssertAssignedComponent(const Component: TComponent);
begin
  Assert(Assigned(Component), 'cannot enumerate nil FComponent');
end;

constructor TComponentOfTypeEnumeratorFactory.Create(const Component: TComponent; const ComponentClass: TComponentClass);
begin
  AssertAssignedComponent(Component);
  FComponent := Component;
  FComponentClass := ComponentClass;
end;

function TComponentOfTypeEnumeratorFactory.GetEnumerator: TComponentOfTypeEnumerator;
begin
  Result := TComponentOfTypeEnumerator.Create(FComponent, FComponentClass);
end;

The factory is a record type which means we will not need to free a potential class instance.
This record approach has two very minor drawbacks: it takes up a tiny bit more stackspace than an object instance reference, and it is possible for users to make copies of it.

The AssertAssignedComponent is in a separate method as it will be reused later on.

The factory now provides the GetEnumerator that is used in the for … in statement.
It returns the actual enumerator class (which is a class, as the for … in statement will automatically free it).
But before we look at the actual class, lets look at the modifications of the basic TComponentEnumerator first:

type
  TComponentEnumerator = class(TObject)
  strict private
    FMinValue: Integer;
    FMaxValue: Integer;
    FValue: Integer;
    FComponent: TComponent;
  public
    constructor Create(const Component: TComponent);
    destructor Destroy; override;
    function GetCurrent: TComponent; virtual;
    function MoveNext: Boolean; virtual;
    property Current: TComponent read GetCurrent;
  end;

The TComponentEnumerator now has the MoveNext and GetCurrent methods marked as virtual so that we can override them in descending enumerator classes.
Which brings me to the actual enumerator class:

type
  TComponentOfTypeEnumerator = class(TComponentEnumerator)
  strict private
    FComponentClass: TComponentClass;
  public
    constructor Create(const Component: TComponent; const ComponentClass: TComponentClass);
    function MoveNext: Boolean; override;
  end;
//...
constructor TComponentOfTypeEnumerator.Create(const Component: TComponent; const ComponentClass: TComponentClass);
begin
  AssertAssignedComponent(Component);
  inherited Create(Component);
  FComponentClass := ComponentClass;
end;

function TComponentOfTypeEnumerator.MoveNext: Boolean;
begin
  if not Assigned(FComponentClass) then
    Result := True
  else
  begin
    while inherited MoveNext do
    begin
      if Current is FComponentClass then
      begin
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;
end;

TComponentOfTypeEnumerator reuses the core logic of TComponentEnumerator, so it only needs to override the MoveNext method.
The reuse goes even further: TComponentOfTypeEnumerator.MoveNext uses TComponentEnumerator.MoveNext, and only adds the logic to check for the right componentclass to filter.

There are many other ways you could add filtering.
What you have to remember is that you need both a factory record and an enumerator class.
The factory remembers the filter and instantiates an enumerator in its GetEnumerator method.

When you read this I’ll be at DelphiLive! and maybe even meet you there!

So here is the full sourcecode for the ComponentHelperUnit:

unit ComponentHelperUnit;

interface

uses
Classes;

type
TComponentEnumerator = class(TObject)
strict private
FMinValue: Integer;
FMaxValue: Integer;
FValue: Integer;
FComponent: TComponent;
public
constructor Create(const Component: TComponent);
destructor Destroy; override;
function GetCurrent: TComponent; virtual;
function MoveNext: Boolean; virtual;
property Current: TComponent read GetCurrent;
end;

TComponentOfTypeEnumerator = class(TComponentEnumerator)
strict private
FComponentClass: TComponentClass;
public
constructor Create(const Component: TComponent; const ComponentClass: TComponentClass);
function MoveNext: Boolean; override;
end;

TComponentOfTypeEnumeratorFactory = record
strict private
FComponent: TComponent;
FComponentClass: TComponentClass;
public
constructor Create(const Component: TComponent; const ComponentClass: TComponentClass);
function GetEnumerator: TComponentOfTypeEnumerator;
end;

TComponentHelper = class helper for TComponent
public
function ComponentsOfType(const ComponentClass: TComponentClass): TComponentOfTypeEnumeratorFactory;
end;

implementation

procedure AssertAssignedComponent(const Component: TComponent);
begin
Assert(Assigned(Component), ‘cannot enumerate nil FComponent’);
end;

{ TComponentHelper }

function TComponentHelper.ComponentsOfType(const ComponentClass: TComponentClass): TComponentOfTypeEnumeratorFactory;
begin
Result := TComponentOfTypeEnumeratorFactory.Create(Self, ComponentClass);
end;

{ TComponentEnumerator }

constructor TComponentEnumerator.Create(const Component: TComponent);
begin
Assert(Assigned(Component), ‘cannot enumerate nil FComponent’);

inherited Create();

Self.FComponent := Component;
FMinValue := 0;
FMaxValue := Component.ComponentCount – 1;
FValue := FMinValue – 1;
end;

destructor TComponentEnumerator.Destroy;
begin

inherited Destroy();
end;

function TComponentEnumerator.GetCurrent: TComponent;
begin
Result := FComponent.Components[FValue];
end;

function TComponentEnumerator.MoveNext: Boolean;
begin
Assert(FMaxValue = FComponent.ComponentCount – 1, ‘ComponentCount changed during enumeration’);
Result := FValue < FMaxValue; if Result then Inc(FValue); end; { TComponentOfTypeEnumerator } constructor TComponentOfTypeEnumerator.Create(const Component: TComponent; const ComponentClass: TComponentClass); begin AssertAssignedComponent(Component); inherited Create(Component); FComponentClass := ComponentClass; end; function TComponentOfTypeEnumerator.MoveNext: Boolean; begin if not Assigned(FComponentClass) then Result := True else begin while inherited MoveNext do begin if Current is FComponentClass then begin Result := True; Exit; end; end; Result := False; end; end; { TComponentOfTypeEnumeratorFactory } constructor TComponentOfTypeEnumeratorFactory.Create(const Component: TComponent; const ComponentClass: TComponentClass); begin AssertAssignedComponent(Component); FComponent := Component; FComponentClass := ComponentClass; end; function TComponentOfTypeEnumeratorFactory.GetEnumerator: TComponentOfTypeEnumerator; begin Result := TComponentOfTypeEnumerator.Create(FComponent, FComponentClass); end; end. [/sourcecode]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.