Implementacja listy Enumerator typów w Delphi
Używam Delphi XE do implementacji enumeratora, który umożliwia filtrowanie elementów listy według typu. Szybko zmontowałem jednostkę testową w następujący sposób:
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.
Kompilowanie tej jednostki nie powiodło się z przerażającym błędem wewnętrznym F2084: D7837. Z pewnością mogę to zrobić bez wyliczenia, ale wolałbym mieć taki dostępny, aby Kod był spójny. Miałem podobny problem z kompilatorem podczas próby zaimplementowania tego na Spring4D, ale pomyślałem, że wydam zwykły, waniliowy Delphi problem tutaj.
Czy ktoś ma alternatywną implementację, która faktycznie kompiluje?
Dzięki.2 answers
Nie chcesz używać IEnumerator
Lepiej zdefiniuj swojego Ienumeratora
IEnumerator<T> = interface
function GetCurrent: T;
function MoveNext: Boolean;
procedure Reset;
property Current: T read GetCurrent;
end;
To samo z IEnumerable. Powiedziałbym, że zdefiniuj swój własny IEnumerable
IEnumerable<T> = interface
function GetEnumerator: IEnumerator<T>;
end;
Jeśli użyjesz tego w TOfTypeEnumerator
Kiedy to zrobisz, zaczniesz widzieć inne błędy kompilatora E2008, E2089 i kilka innych.
Wywołanie po prostu dziedziczone w konstruktorze próbuje wywołać konstruktor z tym samym podpisem w klasie przodka, która nie istnieje. Więc zmień go na dziedziczone Tworzenie.
-
Nie używaj IEnumerable, ale używaj IEnumerable
ponieważ to jest to, co chcesz wyliczyć przez -
Nie używaj metod i odlewy, które są dozwolone tylko dla obiektów lub określają ograniczenie klasy na T i TFilter
-
MoveNext wymaga wyniku
Oto Jednostka kompilacji. Zrobiłem szybki test i wygląda na to, że działa:
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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-23 07:10:16
A worked version using system.IEnumerable<T>
and 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.
Procedura badania:
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;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-27 05:31:26