Fun with enumerators, part 1
Iteration over containers (the 'for ... in' statement) is one of the nice things we Win32 developers got with the BDS 2005. I started using it reluctantly but recently I found out that I'm using and misusing it more and more. I started writing a short article on how to implement multiple enumerators in a class, but somehow it turned into a monster. At the moment I'm up to five parts but actually there may be more. You'll just have to wait and see. If you don't know anything about for...in statement yet and you're running BDS 2005 or later, open up the help and locate Declarations and Statements topic. Inside the lengthy text (near the end), there's an Iteration Over Containers Using For statements subtopic that explains this theme. BDS 2006 users may simply click this link to open the subtopic in question. This is not a tutorial on iteration, but still a short demo wouldn't hurt. Following simple program adds three items to a string list and then uses for...in construct to write those three lines out. Then it copies first line to a string and uses another for...in construct to iterate over all characters in the string. Output is shown just below the code. program ForIteratorDemo; How are enumerators made?To iterate over something, the target must know how to create an enumerator. Enumerators are the mechanism used by the compiler to implement iterator support for any class, record or interface (in addition to some system types as sets and strings, where iterator support is built into the compiler). Creating an enumerator is simple.The following instructions are copied directly from Delphi help:
For a practical demonstration, you can open Delphi's Classes.pas or, if you don't have Delphi sources or if you are still runnning pre-2005 Delphi, my own GpLists, which implements two enumerators. The one supporting for...in construct on the TGpIntegerList class is shown below. type The code is quite simple. TGpIntegerList implements GetEnumerator method, which creates an instance of the TGpIntegerListEnumerator class. That one in turn implements GetCurrent and MoveNext functions and Current property. The trick when writing an enumerator is to keep in mind that MoveNext is called before the GetCurrent. You can assume that compiler-generated iteration loop looks very similar to this pseudocode: enumerator := list.GetEnumerator; Enough for today. You probably didn't learn anything new but that may change tomorrow when I'll work on a much more interesting task - how to add a second enumerator to a class that already implements one.
Labels: Delphi, enumerators, programming, source code |
3 Comments:
Very inspiring hint. For the fun I made a TDataSetForIn wich allow to write for any TDataSet:
procedure TMyForm.Test;
var
DS: TDataSetForIn;
begin
for DS in TDataSetForIn( MyQuery) do
Memo1.Lines.Add( DS.FieldByName('ID').AsString);
end;
instead of the traditional:
with MyQuery do begin
First;
while not EOF do begin
Memo1.Lines.Add( DS.FieldByName('ID').AsString);
Next;
end;
end;
Would be nice that CodeGear could add directly GetEnumerator in TDataSet.
Code is below if someone interested.
Renaud.
type
TDataSetForIn = class;
TDataSetEnumerator = class
private
FDataSet: TDataSetForIn;
FDoFirst: boolean;
public
constructor Create( ADataSet: TDataSetForIn);
function GetCurrent: TDataSetForIn;
function MoveNext: boolean;
property Current: TDataSetForIn read GetCurrent;
end;
TDataSetForIn = class ( TDataSet)
public
function GetEnumerator: TDataSetEnumerator;
end;
implementation
{ TDataSetEnumerator }
constructor TDataSetEnumerator.Create(ADataSet: TDataSetForIn);
begin
FDoFirst := True;
FDataSet := ADataSet;
FDataSet.Open;
end;
function TDataSetEnumerator.GetCurrent: TDataSetForIn;
begin
Result := FDataSet;
end;
function TDataSetEnumerator.MoveNext: boolean;
begin
if FDoFirst then begin
FDoFirst := False;
FDataSet.First;
end
else FDataSet.Next;
Result := not FDataSet.EOF;
end;
{ TDataSetForIn }
function TDataSetForIn.GetEnumerator: TDataSetEnumerator;
begin
Result := TDataSetEnumerator.Create( Self);
end;
Actually, you don't have to write TDataSetForIn class at all (but I'll only cover that in parts 4 and 5).
Well, I'd like to see that. So I wait for your next posts.
Thanks,
Renaud.
Post a Comment
Links to this post:
Create a Link
<< Home