Вопрос по generics – Реализация перечислителя списка OfType в Delphi

21

Я использую Delphi XE для реализации перечислителя, который позволяет фильтровать элементы списка по типу. Я быстро собрал тестовый блок следующим образом:

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.

У кого-нибудь есть альтернативная реализация, которая на самом деле компилируется?

Благодарю.

Проверьте комментарии, сделанные USc в вашем отчете qc. The_Fox
qc.embarcadero.com/wc/qcmain.aspx?d=105719 Leonardo Herrera
Только что отправлено в КК - # 105719. Благодарю. Louis Kleiman
То же самое в XE2. Отправить в КК. Дженерики все еще в значительной степени непригодны. David Heffernan

Ваш Ответ

2   ответа
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;
32

Вы не хотите использовать IEnumerator & lt; T & gt; с System.pas, поверь мне. Эта вещь приносит много проблем, потому что она наследуется от IEnumerator, и поэтому этот метод GetCurrent имеет разные результаты (TObject для IEnumerator и T для IEnumerator & lt; T & gt;).

Лучше определите свой собственный IEnumerator & lt; T & gt ;:

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

То же самое с IEnumerable. Я бы сказал, определите свой собственный IEnumerable & lt; T & gt ;:

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

Если вы используете это в своем TOfTypeEnumerator & lt; T, TFilter & gt; Вы можете удалить пункты разрешения метода, вызывающие ICE.

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

  • calling just inherited in your constructor tries to call the constructor with the same signature in your ancestor class which does not exist. So change it to inherited Create.

  • don't use IEnumerable but use IEnumerable<TFilter> because that is what your want to enumerator over

  • don't use methods and casts that are only allowed for objects or specify the class constraint on T and TFilter

  • MoveNext needs a Result

Вот модуль компиляции. Сделал быстрый тест, и, кажется, работает:

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.
Error: User Rate Limit Exceeded

Похожие вопросы