I am using Delphi XE to implement an enumerator that allows filtering the elements of the list by type. I have quickly assembled a test unit as follows:
unit uTestList;
interface
uses Generics.Collections;
type
TListItemBase = class(TObject)
end; { TListItemBase }
TListItemChild1 = class(TListItemBase)
end;
TListItemChild2 = class(TListItemBase)
end;
TTestList<T : TListItemBase> = class;
TOfTypeEnumerator<T, TFilter> = class(TInterfacedObject, IEnumerator<TFilter>)
private
FTestList : TList<T>;
FIndex : Integer;
protected
constructor Create(Owner : TList<T>); overload;
function GetCurrent : TFilter;
function MoveNext : Boolean;
procedure Reset;
function IEnumerator<TFilter>.GetCurrent = GetCurrent;
function IEnumerator<TFilter>.MoveNext = MoveNext;
procedure IEnumerator<TFilter>.Reset = Reset;
end;
TOfTypeEnumeratorFactory<T, TFilter> = class(TInterfacedObject, IEnumerable)
private
FTestList : TList<T>;
public
constructor Create(Owner : TList<T>); overload;
function GetEnumerator : TOfTypeEnumerator<T, TFilter>;
end;
TTestList<T : TListItemBase> = class(TList<T>)
public
function OfType<TFilter : TListItemBase>() : IEnumerable;
end; { TTestList }
implementation
{ TOfTypeEnumerator<T, TFilter> }
constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>);
begin
inherited;
FTestList := Owner;
FIndex := -1;
end;
function TOfTypeEnumerator<T, TFilter>.GetCurrent: TFilter;
begin
Result := TFilter(FTestList[FIndex]);
end;
function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean;
begin
Inc(FIndex);
while ((FIndex < FTestList.Count)
and (not FTestList[FIndex].InheritsFrom(TFilter))) do
begin
Inc(FIndex);
end; { while }
end;
{ TOfTypeEnumeratorFactory<T, TFilter> }
constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>);
begin
inherited;
FTestList := Owner;
end;
function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: TOfTypeEnumerator<T, TFilter>;
begin
Result := TOfTypeEnumerator<T,TFilter>.Create(FTestList);
end;
{ TTestList<T> }
function TTestList<T>.OfType<TFilter>: IEnumerable;
begin
Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self);
end;
end.
Compiling this unit fails with the dreaded F2084 Internal Error: D7837. I can certainly do this without an enumerator, but I'd rather have one available to make the code consistent. I had a similar compiler problem when trying to implement this on top of Spring4D, but figured I would put out a plain, vanilla Delphi issue here.
Does anyone have an alternate implementation that actually compiles?
Thanks.
You don't want to use the IEnumerator<T> from System.pas, trust me. That thing brings along so much trouble because it inherits from IEnumerator and so has that GetCurrent method with different results (TObject for IEnumerator and T for IEnumerator<T>).
Better define your own IEnumerator<T>:
Same with IEnumerable. I would say define your own IEnumerable<T>:
If you use that in your TOfTypeEnumerator<T, TFilter> you can remove the method resolution clauses causing the ICE.
When you do that you will start seeing other compiler errors E2008, E2089 and some more.
calling just inherited in your constructor tries to call the constructor with the same signature in your ancestor class which does not exist. So change it to inherited Create.
don't use IEnumerable but use IEnumerable<TFilter> because that is what your want to enumerator over
don't use methods and casts that are only allowed for objects or specify the class constraint on T and TFilter
MoveNext needs a Result
Here is the compiling unit. Did a quick test and it seems to work:
A worked version using
system.IEnumerable<T>
andsystem.IEnumerator<T>
A test procedure: