Fun with enumerators, part 3 - parameterized enumerators
In first two installments of this series, we took a look at for..in statement, described Delphi support for custom enumerators and demonstrated one possible way to add additional enumerator to a class that already implements one.
Today we'll implement a parameterized enumerator. Enumerators of this kind can be especially handy when used as filters. For example, an enumerator that takes a class reference as a parameter can be used to filter a TObjectList containing objects of many different classes.
IOW, we would like to write a following construct: for obj in list.Enum(TSomeClass) do. Clearly, Enum cannot be a property anymore, but it can be a function.
We can still use an enumerator factory object stored inside our class (as in the Part 2). Besides that, we must implement enumeration function that will take one or more parameters, preserve them inside the factory object and return the factory object so that Delphi compiler can call its GetEnumerator function.
Sounds complicated but in reality it really is simple.
First we have to add an internal factory object and an enumeration function to the TCustomStringList class.
TCustomStringList = class(TStringList)
Enumeration function SkipEveryNth takes one parameter and passes it to the factory object. Then it returns this factory object.
We also need a new factory class. This is an extended version of factory class from Part 2. It must implement storage for all enumerator parameters. In this case, this storage is implemented via property Skip.
TCSLEnumEveryNthFactory = class
As you can see, GetEnumerator was also changed - in addition to passing FOwner to the created enumerator, it also passes current value of the Skip property.
New enumerator is very similar to the one from the Part 2, except that it advances list index by the specified ammount (instead of advancing it by 2).
TCSLEnumEveryNth = class
We can now write some test code.
procedure TfrmFunWithEnumerators.btnSkipEveryThirdClick(Sender: TObject);
Delphi compiler will translate this for..in roughly into
enumerator := FslTest.SkipEveryNth(3).GetEnumerator;
So what is going on here?
Test code result:
Tomorrow we'll do something even more interesting - we'll add new enumerator to existing class without creating a derived class.