在 Delphi 中实现 List Enumerator OfType<T>

Posted

技术标签:

【中文标题】在 Delphi 中实现 List Enumerator OfType<T>【英文标题】:Implementing List Enumerator OfType<T> in Delphi 【发布时间】:2012-05-26 07:32:10 【问题描述】:

我正在使用 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 问题。

有没有人有实际编译的替代实现?

谢谢。

【问题讨论】:

在 XE2 中相同。提交给 QC。泛型仍然几乎无法使用。 刚刚提交给 QC -- #105719。谢谢。 qc.embarcadero.com/wc/qcmain.aspx?d=105719 在您的质量控制报告中检查 USc 制造的 cmets。 【参考方案1】:

您不想使用 System.pas 中的 IEnumerator,相信我。那件事带来了很多麻烦,因为它继承自 IEnumerator,因此具有不同结果的 GetCurrent 方法(TObject 代表 IEnumerator,T 代表 IEnumerator)。

最好定义自己的 IEnumerator

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

与 IEnumerable 相同。我会说定义你自己的 IEnumerable:

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

如果您在 TOfTypeEnumerator 中使用它,您可以删除导致 ICE 的方法解析子句。

当您这样做时,您将开始看到其他编译器错误 E2008、E2089 等等。

在您的构造函数中调用刚刚继承的函数会尝试调用具有相同签名的构造函数,该签名在您的祖先类中不存在。所以把它改成继承的Create。

不要使用 IEnumerable,而是使用 IEnumerable,因为这是您想要枚举的对象

不要使用只允许用于对象的方法和强制转换,或者在 T 和 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.

【讨论】:

说得好。不幸的是,我遇到您的建议为时已晚;系统单元中的接口实现起来真的很痛苦! 嘿,我看到了你关于 Lambda 和 delphi 的博客文章。我试着看看如何联系你。我似乎无法登录或注册entwickler-ecke.de/viewtopic.php?t=103194&view=df 以获取文件。你有副本吗??【参考方案2】:

使用system.IEnumerable&lt;T&gt;system.IEnumerator&lt;T&gt; 的工作版本

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;

【讨论】:

以上是关于在 Delphi 中实现 List Enumerator OfType<T>的主要内容,如果未能解决你的问题,请参考以下文章

在delphi中实现控件的拖拽

请问如何在delphi中实现多选打印功能!

在 Delphi 中实现 OAuth 提供程序

TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

如何在自定义 delphi 组件中实现 stringlist 属性?

哪些选项可用于使用 SOAP 在 Delphi 中实现长轮询?