I have defined a collection derived from TDictionary, and need to define a custom enumerator that apply an additional filter.
I'm stuck as I can't access the TDictionary FItems array (it is private) so I can't define the MoveNext method
How would you proceed to redefine a filtered enumerator on a class derived from TDictionary?
Here's a simple code to illustrate what I want to do:
TMyItem = class(TObject)
public
IsHidden:Boolean; // The enumerator should not return hidden items
end;
TMyCollection<T:TMyItem> = class(TDictionary<integer,T>)
public
function GetEnumerator:TMyEnumerator<T>; // A value filtered enumerator
type
TMyEnumerator = class(TEnumerator<T>)
private
FDictionary: TMyCollection<integer,T>;
FIndex: Integer;
function GetCurrent: T;
protected
function DoGetCurrent: T; override;
function DoMoveNext: Boolean; override;
public
constructor Create(ADictionary: TMyCollection<integer,T>);
property Current: T read GetCurrent;
function MoveNext: Boolean;
end;
end;
function TMyCollection<T>.TMyEnumerator.MoveNext: Boolean;
begin
// In below code, FIndex is not accessible, so I can't move forward until my filter applies
while FIndex < Length(FDictionary.FItems) - 1 do
begin
Inc(FIndex);
if (FDictionary.FItems[FIndex].HashCode <> 0)
and not(FDictionary.FItems[FIndex].IsHidden) then // my filter
Exit(True);
end;
Result := False;
end;
You can base your Enumerator on TDictionary
's enumerator, so you don't actually need access to FItems
. This works even if you write a wrapper class around TDictionary
as Barry suggests. The enumerator would look like this:
TMyEnumerator = class
protected
BaseEnumerator: TEnumerator<TPair<Integer, T>>; // using the key and value you used in your sample
public
function MoveNext:Boolean;
property Current:T read GetCurrent;
end;
function TMyEnumerator.MoveNext:Boolean;
begin
Result := BaseEnumerator.MoveNext;
while Result and (not (YourTestHere)) do // ie: the base enumerator returns everything, reject stuff you don't like
Result := BaseEnumerator.MoveNext;
end;
function TMyEnumerator.Current: T;
begin
Result := BaseEnumerator.Current.Value; // Based on your example, it's value you want to extract
end;
And here's a complete, 100 lines console application that demonstrates this:
program Project23;
{$APPTYPE CONSOLE}
uses
SysUtils, Generics.Collections;
type
TMyType = class
public
Int: Integer;
constructor Create(anInteger:Integer);
end;
TMyCollection<T:TMyType> = class(TDictionary<integer,T>)
strict private
type
TMyEnumerator = class
protected
BaseEnum: TEnumerator<TPair<Integer,T>>;
function GetCurrent: T;
public
constructor Create(aBaseEnum: TEnumerator<TPair<Integer,T>>);
destructor Destroy;override;
function MoveNext:Boolean;
property Current:T read GetCurrent;
end;
public
function GetEnumerator: TMyEnumerator;
end;
{ TMyCollection<T> }
function TMyCollection<T>.GetEnumerator: TMyEnumerator;
begin
Result := TMyEnumerator.Create(inherited GetEnumerator);
end;
{ TMyType }
constructor TMyType.Create(anInteger: Integer);
begin
Int := anInteger;
end;
{ TMyCollection<T>.TMyEnumerator }
constructor TMyCollection<T>.TMyEnumerator.Create(aBaseEnum: TEnumerator<TPair<Integer, T>>);
begin
BaseEnum := aBaseEnum;
end;
function TMyCollection<T>.TMyEnumerator.GetCurrent: T;
begin
Result := BaseEnum.Current.Value;
end;
destructor TMyCollection<T>.TMyEnumerator.Destroy;
begin
BaseEnum.Free;
inherited;
end;
function TMyCollection<T>.TMyEnumerator.MoveNext:Boolean;
begin
Result := BaseEnum.MoveNext;
while Result and ((BaseEnum.Current.Value.Int mod 2) = 1) do
Result := BaseEnum.MoveNext;
end;
var TMC: TMyCollection<TMyTYpe>;
V: TMyType;
begin
try
TMC := TMyCollection<TMyType>.Create;
try
// Fill TMC with some values
TMC.Add(1, TMyType.Create(1));
TMC.Add(2, TMyType.Create(2));
TMC.Add(3, TMyType.Create(3));
TMC.Add(4, TMyType.Create(4));
TMC.Add(5, TMyType.Create(5));
TMC.Add(6, TMyType.Create(6));
TMC.Add(7, TMyType.Create(7));
TMC.Add(8, TMyType.Create(8));
// Filtered-enum
for V in TMC do
WriteLn(V.Int);
ReadLn;
finally TMC.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.