pcplayer:
Enumerating collections in Win32 for...in...语法
[阅读: 446] 2005-01-22 17:47:15
Enumerating collections in Win32
Delphi 2005 introduced the capability to enumerate collections. Some of the builtin types and collections found in the language itself and in the VCL already support enumeration like:
Arrays
const
MyArr : Array[0..3] of char = ('1','2','3','4');
var
C : Char;
S : String;
begin
for C in MyArr do
S := S + C;
ShowMessage(S);
end;
Sets
const
MySet: set of char = ['1','2','3','4'];
var
C : Char;
S : String;
begin
for C in MySet do
S := S + C;
ShowMessage(S);
end;
Strings
const
S : String = 'This is an insteresting string';
var
C : Char;
cnt: Integer;
begin
cnt := 0;
for C in S do
if UpCase(C) = 'E' then
Inc (cnt);
ShowMessage(Format('Your string has %d letters "e"',[cnt]));
end;
TStringLists
var
S : String;
begin
for S in Memo1.Lines do
ListBox1.Items.Add(S);
end;
Nice, isn't it? What if you wanted to enumerate your own collections? Let's say you have the following classes:
TPerson = class
Name: String;
DateOfBirth: TDateTime;
Gender : Char;
constructor Create (AName: String; ADateOfBirth: TDateTime; AGender: Char);
end;
TPeopleList = class
strict private
List : TList;
function GetCount: Integer;
function GetPerson(idx: Integer): TPerson;
procedure SetPerson(idx: Integer; const Value: TPerson);
public
function Add (Person: TPerson): Integer;
procedure Delete (idx: Integer);
property Person [idx: Integer] : TPerson read GetPerson write SetPerson; default;
property Count: Integer read GetCount;
end;
Wouldn't it be great if you could enumerate all the people in a TPeopleList using the new for..in syntax? Don't worry... it's easy :) In order for a collection to be enumerable, it must implement a public instance method called GetEnumerator, and this method must return a class, interface or record type with a MoveNext method and a Current property (with a public GetCurrent read-access method). So, in our case, we should create a TPeopleEnumerator like this:
TPeopleEnumerator = class
strict private
CurrentPos: Integer;
PeopleList : TPeopleList;
public
constructor Create (AList : TPeopleList);
function GetCurrent: TPerson;
function MoveNext :Boolean;
property Current : TPerson read GetCurrent;
end;
And here are its methods implementations:
{ TPeopleEnumerator }
constructor TPeopleEnumerator.Create(AList: TPeopleList);
begin
inherited Create;
PeopleList := AList;
CurrentPos := -1;
end;
function TPeopleEnumerator.GetCurrent: TPerson;
begin
Result := PeopleList[CurrentPos];
end;
function TPeopleEnumerator.MoveNext: Boolean;
begin
if CurrentPos < PeopleList.Count-1 then
begin
Inc(CurrentPos);
Result := True;
end
else
Result := False;
end;
Now, declare and implement the GetEnumerator method in the TPeopleList class:
function TPeopleList.GetEnumerator: TPeopleEnumerator;
begin
Result := TPeopleEnumerator.Create(Self);
end;
That's it! Here's a sample code snippet to enumerate our TPeopleList:
var
PeopleList : TPeopleList;
Person: TPerson;
begin
PeopleList := TPeopleList.Create; try
PeopleList.Add(TPerson.Create('Daniel',StrToDate('03/15/1973'),'M'));
PeopleList.Add(TPerson.Create('Monica',StrToDate('05/05/1973'),'F'));
for Person in PeopleList do
ListBox1.Items.Add(Person.Name);
finally
PeopleList.Free; end;
end;