2012-05-18 3 views
20

Я использую Delphi XE для реализации перечислителя, который позволяет фильтровать элементы списка по типу. Я быстро собрал тестовый блок следующим образом:Реализация List Enumerator OfType <T> в 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. 

Компиляция блок терпит неудачу с ужасной F2084 Внутренняя ошибка: D7837. Я могу, конечно, сделать это без счетчика, но я предпочел бы, чтобы один из них смог сделать код последовательным. У меня была аналогичная проблема с компилятором при попытке реализовать это поверх Spring4D, но я подумал, что я бы выпустил простой, ванильный вопрос Delphi.

Есть ли у кого-то альтернативная реализация, которая фактически компилируется?

Спасибо.

+4

же в XE2. Отправить в КК. Дженерики все еще довольно непригодны для использования. –

+1

Только что отправлен в QC - # 105719. Благодарю. –

+1

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

ответ

31

Вы не хотите использовать IEnumerator <T> из System.pas, поверьте мне. Эта вещь приносит столько проблем, потому что она наследует от IEnumerator и поэтому имеет этот метод GetCurrent с разными результатами (TObject для IEnumerator и T для IEnumeratorT >).

лучше определить свои собственные IEnumerator <T>:

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

То же самое с IEnumerable. Я бы сказал, определить свой собственный IEnumerable <T>:

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

Если вы используете это в вашем TOfTypeEnumerator < T, TFilter > вы можете удалить пункты разрешения метод вызывает ДВС.

Когда вы это сделаете, вы увидите другие ошибки компилятора E2008, E2089 и некоторые другие.

  • вызов, только что унаследованный в вашем конструкторе, пытается вызвать конструктор с той же сигнатурой в классе вашего предка, который не существует. Поэтому измените его на унаследованный Create.

  • не использовать IEnumerable, но использовать IEnumerable <TFilter>, потому что это то, что хочет, чтобы Enumerator над

  • не использовать методы и слепки, которые разрешены только для объектов или указать ограничение класса на Т и 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. 
+0

Хорошо сказано. Я, к сожалению, слишком поздно встретил ваш совет; интерфейсы в системном блоке - настоящая боль для реализации! – AdrianGW

1

проработанный вариант с использованием 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;