delphi generics enumerable

Implementando List Enumerator OfType<T> en Delphi



generics enumerable (2)

No quiere usar el IEnumerator <T> de System.pas, créame. 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 defina su propio IEnumerator <T>:

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

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

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

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

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

  • la invocación heredada en su constructor intenta llamar al constructor con la misma firma en su clase ancestra que no existe. Así que cámbialo a Create heredado.

  • no use IEnumerable, pero use IEnumerable <TFilter> porque eso es lo que desea enumerar sobre

  • no use métodos y moldes que solo están permitidos para objetos o especifique la restricción de clase en T y TFilter

  • MoveNext necesita un resultado

Aquí está la unidad de compilación. Hice una prueba rápida y parece funcionar:

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.

Estoy usando Delphi XE para implementar un enumerador que permita filtrar los elementos de la lista por tipo. He montado rápidamente una unidad de prueba de la siguiente manera:

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.

La compilación de esta unidad falla con el temido error interno F2084: 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.


Una versión trabajada que usa 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;