2012-05-18 6 views
20

Używam Delphi XE do implementacji modułu wyliczającego, który umożliwia filtrowanie elementów listy według typu. I szybko zmontowane jako jednostkę badawczą, co następuje:Implementing List Enumerator OfType <T> w Delphi

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. 

Kompilacja z tego urządzenia nie bał F2084 wewnętrzny błąd: D7837. Z pewnością mogę to zrobić bez modułu wyliczającego, ale wolałbym mieć jeden, aby kod był spójny. Miałem podobny problem z kompilatorem przy próbie wdrożenia tego na Spring4D, ale pomyślałem, że wypuściłbym tutaj prosty, waniliowy problem Delphi.

Czy ktoś ma alternatywną implementację, która faktycznie kompiluje?

Dzięki.

+4

samo w XE2. Prześlij do QC. Generics są nadal w dużym stopniu bezużyteczne. –

+1

Właśnie przesłano do QC - # 105719. Dzięki. –

+1

http://qc.embarcadero.com/wc/qcmain.aspx?d=105719 –

Odpowiedz

31

Nie chcesz używać IEnumerator <T> z System.pas, zaufaj mi. Ta rzecz przynosi tyle kłopotów, ponieważ dziedziczy po IEnumeratorze, podobnie jak metoda GetCurrent z różnymi wynikami (TObject for IEnumerator i T for IEnumerator <T>).

lepiej zdefiniować swoją IEnumerator <T>:

IEnumerator<T> = interface 
    function GetCurrent: T; 
    function MoveNext: Boolean; 
    procedure Reset; 
    property Current: T read GetCurrent; 
end; 

samo z IEnumerable. Powiedziałbym zdefiniować własną IEnumerable <T>:

IEnumerable<T> = interface 
    function GetEnumerator: IEnumerator<T>; 
end; 

Jeśli używasz, że w TOfTypeEnumerator < T TFilter > można usunąć klauzule rozdzielczości metoda powoduje lodzie.

Gdy to zrobisz, zaczniesz widzieć inne błędy kompilatora E2008, E2089 i kilka innych.

  • wzywając właśnie odziedziczył w konstruktorze próbuje wywołać konstruktor z tym samym podpisem w klasie przodka, który nie istnieje. Więc zmień go na dziedziczony Utwórz.

  • nie używaj IEnumerable ale używać IEnumerable <TFilter> ponieważ to, co twój chce wyliczający nad

  • nie używać metod i odlewów, które są dozwolone tylko dla obiektów lub określić klasę ograniczenia na T i TFilter

  • MoveNext potrzebuje wyniku

Oto jednostka kompilacji. Zrobiłem szybki test i wydaje się 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. 
+0

Dobrze powiedziane. Niestety zbyt późno spotkałem się z twoją radą; interfejsy w jednostce systemowej są prawdziwym trudem do wdrożenia! – AdrianGW

1

ugniataniu wersję używając system.IEnumerable<T> i 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 testu:

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;