Реализация перечислителя списков OfType в Delphi
Я использую Delphi XE для реализации перечислителя, который позволяет фильтровать элементы списка по типу. Я быстро собрал тестовый блок следующим образом:--2-->
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.
компиляция этого устройства завершается с ужасной внутренней ошибкой F2084: D7837. Я, конечно, могу сделать это без перечислителя, но я бы предпочел иметь его, чтобы сделать код согласованным. У меня была аналогичная проблема компилятора при попытке реализовать это поверх Spring4D, но я решил, что потушу равнину, ванильный выпуск Delphi здесь.
есть ли у кого-нибудь альтернативная реализация, которая фактически компилируется?
спасибо.
2 ответов
вы не хотите использовать IEnumerator
лучше определить свой собственный IEnumerator
IEnumerator<T> = interface
function GetCurrent: T;
function MoveNext: Boolean;
procedure Reset;
property Current: T read GetCurrent;
end;
то же самое с IEnumerable. Я бы сказал, определите свой собственный IEnumerable
IEnumerable<T> = interface
function GetEnumerator: IEnumerator<T>;
end;
Если вы используете это в своем TOfTypeEnumerator
когда вы это сделаете, вы начнете видеть другие ошибки компилятора E2008, E2089 и некоторые другие.
вызов только что унаследованного в вашем конструкторе пытается вызвать конструктор с той же сигнатурой в вашем классе предка, который не существует. Поэтому измените его на унаследованное Create.
не используйте IEnumerable, но используйте IEnumerable
, потому что это то, что вы хотите enumerator over не используйте методы и приведения, разрешенные только для объектов, или укажите ограничение класса на T и TFilter
MoveNext нужен результат
вот блок компиляции. Сделал быстрый тест и, кажется, работает:
unit uTestList;
interface
uses
Generics.Collections;
type
IEnumerator<T> = interface
function GetCurrent: T;
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
IEnumerable<T> = interface
function GetEnumerator: IEnumerator<T>;
end;
TOfTypeEnumerator<T: class; TFilter: class> = class(TInterfacedObject, IEnumerator<TFilter>)
private
FTestList: TList<T>;
FIndex: Integer;
protected
constructor Create(Owner: TList<T>); overload;
function GetCurrent: TFilter;
function MoveNext: Boolean;
end;
TOfTypeEnumeratorFactory<T: class; TFilter: class> = class(TInterfacedObject, IEnumerable<TFilter>)
private
FTestList: TList<T>;
public
constructor Create(Owner: TList<T>); overload;
function GetEnumerator: IEnumerator<TFilter>;
end;
TTestList<T: class> = class(TList<T>)
public
function OfType<TFilter: class>: IEnumerable<TFilter>;
end;
implementation
{ TOfTypeEnumerator<T, TFilter> }
constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>);
begin
inherited Create;
FTestList := Owner;
FIndex := -1;
end;
function TOfTypeEnumerator<T, TFilter>.GetCurrent: TFilter;
begin
Result := TFilter(TObject(FTestList[FIndex]));
end;
function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean;
begin
repeat
Inc(FIndex);
until (FIndex >= FTestList.Count) or FTestList[FIndex].InheritsFrom(TFilter);
Result := FIndex < FTestList.Count;
end;
{ TOfTypeEnumeratorFactory<T, TFilter> }
constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>);
begin
inherited Create;
FTestList := Owner;
end;
function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: IEnumerator<TFilter>;
begin
Result := TOfTypeEnumerator<T, TFilter>.Create(FTestList);
end;
{ TTestList<T> }
function TTestList<T>.OfType<TFilter>: IEnumerable<TFilter>;
begin
Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self);
end;
end.
работающая версия с использованием system.IEnumerable<T>
и system.IEnumerator<T>
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 : class; TFilter : class> = class(TInterfacedObject, IEnumerator<TFilter>, IEnumerator)
private
FTestList : TList<T>;
FIndex : Integer;
protected
constructor Create(Owner : TList<T>); overload;
function GetCurrent: TObject;
function GenericGetCurrent : TFilter;
function MoveNext : Boolean;
procedure Reset;
function IEnumerator<TFilter>.GetCurrent = GenericGetCurrent;
end;
TOfTypeEnumeratorFactory<T : class; TFilter : class> = class(TInterfacedObject, IEnumerable<TFilter>, IEnumerable)
private
FTestList : TList<T>;
public
constructor Create(Owner : TList<T>); overload;
function GetEnumerator : IEnumerator;
function GenericGetEnumerator : IEnumerator<TFilter>;
function IEnumerable<TFilter>.GetEnumerator = GenericGetEnumerator;
end;
TTestList<T : TListItemBase> = class(TList<T>)
public
function OfType<TFilter : TListItemBase>() : IEnumerable<TFilter>;
end; { TTestList }
implementation
{ TOfTypeEnumerator<T, TFilter> }
constructor TOfTypeEnumerator<T, TFilter>.Create(Owner: TList<T>);
begin
inherited Create;
FTestList := Owner;
FIndex := -1;
end;
function TOfTypeEnumerator<T, TFilter>.GenericGetCurrent: TFilter;
begin
Result := TFilter(TObject(FTestList[FIndex]));
end;
function TOfTypeEnumerator<T, TFilter>.GetCurrent: TObject;
begin
Result := TObject( FTestList[FIndex] );
end;
function TOfTypeEnumerator<T, TFilter>.MoveNext: Boolean;
begin
repeat
Inc(FIndex);
until (FIndex >= FTestList.Count) or FTestList[FIndex].InheritsFrom(TFilter);
Result := FIndex < FTestList.Count;
end;
procedure TOfTypeEnumerator<T, TFilter>.Reset;
begin
FIndex := -1;
end;
{ TOfTypeEnumeratorFactory<T, TFilter> }
constructor TOfTypeEnumeratorFactory<T, TFilter>.Create(Owner: TList<T>);
begin
inherited Create;
FTestList := Owner;
end;
function TOfTypeEnumeratorFactory<T, TFilter>.GetEnumerator: IEnumerator;
begin
Result := GenericGetEnumerator;
end;
function TOfTypeEnumeratorFactory<T, TFilter>.GenericGetEnumerator: IEnumerator<TFilter>;
begin
Result := TOfTypeEnumerator<T,TFilter>.Create(FTestList);
end;
{ TTestList<T> }
function TTestList<T>.OfType<TFilter>: IEnumerable<TFilter>;
begin
Result := TOfTypeEnumeratorFactory<T,TFilter>.Create(self);
end;
end.
проверка:
var
MyElem: TListItemBase;
MyElem1: TListItemChild1;
MyElem2: TListItemChild2;
begin
Memo1.Clear;
for MyElem in FTestList.OfType<TListItemBase>() do
begin
Memo1.Lines.Add('----------');
end;
for MyElem1 in FTestList.OfType<TListItemChild1>() do
begin
Memo1.Lines.Add('==========');
end;
for MyElem2 in FTestList.OfType<TListItemChild2>() do
begin
Memo1.Lines.Add('++++++++++');
end;