2012-05-18 12 views
20

Estoy utilizando Delphi XE para implementar un enumerador que permite filtrar los elementos de la lista por tipo. He montado rápidamente en una unidad de prueba de la siguiente manera:Implementando lista Enumerator OfType <T> en 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. 

Compilación de esta unidad no funciona con la temida F2084 Error interno: D7837. Ciertamente puedo hacer esto sin un enumerador, pero prefiero tener uno disponible para que el código sea coherente. Tuve un problema de compilación similar cuando traté de implementar esto sobre Spring4D, pero pensé que pondría un problema de Delphi simple y corriente aquí.

¿Alguien tiene una implementación alternativa que realmente compila?

Gracias.

+4

mismo en XE2. Presentar a QC. Los genéricos aún son bastante inutilizables. –

+1

Recién enviado a QC - # 105719. Gracias. –

+1

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

Respuesta

31

No desea utilizar el IEnumerator <T> de System.pas, confíe en mí. Esa cosa trae tantos problemas porque hereda de IEnumerator y también tiene el método GetCurrent con diferentes resultados (TObject para IEnumerator y T para IEnumerator <T>).

mejor definir su propio IEnumerator <T>:

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

mismo con IEnumerable. Yo diría que definir su propio IEnumerable <T>:

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

Si utiliza eso en su TOfTypeEnumerator < T, TFilter > puede eliminar las cláusulas de resolución de método haciendo que el ICE.

Cuando lo haga, comenzará a ver otros errores del compilador E2008, E2089 y algunos más.

  • llamando simplemente heredada en su constructor intenta llamar al constructor con la misma firma en su clase ancestro que no existe. Así que cámbialo a Create heredado.

  • no utilizan IEnumerable pero el uso IEnumerable <TFilter> porque eso es lo que tu quieres empadronador sobre

  • no utilizan métodos y yesos que sólo se permiten para los objetos o especificar la restricción de clase en T y TFilter

  • MoveNext necesita un resultado

Aquí está la unidad de compilación. Hizo una prueba rápida y parece que funciona:

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

Bien dicho. Desafortunadamente encontré tu consejo demasiado tarde; las interfaces en la unidad del sistema son un verdadero dolor para implementar! – AdrianGW

1

Una versión trabajó usando system.IEnumerable<T> y 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. 

un procedimiento de prueba:

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; 
Cuestiones relacionadas