在TList <TMyObject>上实现过滤枚举器的更好方法

Cos*_*und 10 delphi foreach filter enumerator delphi-2010

使用Delphi 2010,假设我有一个声明如下的类:

TMyList = TList<TMyObject>
Run Code Online (Sandbox Code Playgroud)

对于这个列表,Delphi为我们提供了一个枚举器,所以我们可以这样写:

var L:TMyList;
    E:TMyObject;
begin
  for E in L do ;
end;
Run Code Online (Sandbox Code Playgroud)

麻烦的是,我想写这个:

var L:TMyList;
    E:TMyObject;
begin
  for E in L.GetEnumerator('123') do ;
end;
Run Code Online (Sandbox Code Playgroud)

也就是说,我希望能够使用某些标准为同一列表提供多个枚举器.不幸的是,实现for X in Z需要存在一个函数Z.GetEnumerator,没有参数,返回给定的枚举器!为了解决这个问题,我正在定义一个实现"GetEnumerator"函数的接口,然后我实现了一个实现接口的类,最后我在TMyList上编写了一个返回接口的函数!而且我正在返回一个界面,因为我不想被手动释放非常简单的类所困扰......任何方式,这需要很多打字.这是这样的:

TMyList = class(TList<TMyObject>)
protected

  // Simple enumerator; Gets access to the "root" list
  TSimpleEnumerator = class
  protected
  public
    constructor Create(aList:TList<TMyObject>; FilterValue:Integer);

    function MoveNext:Boolean; // This is where filtering happens
    property Current:TTipElement;
  end;

  // Interface that will create the TSimpleEnumerator. Want this
  // to be an interface so it will free itself.
  ISimpleEnumeratorFactory = interface
    function GetEnumerator:TSimpleEnumerator;
  end;

  // Class that implements the ISimpleEnumeratorFactory
  TSimpleEnumeratorFactory = class(TInterfacedObject, ISimpleEnumeratorFactory)
    function GetEnumerator:TSimpleEnumerator;
  end;

public
  function FilteredEnum(X:Integer):ISimpleEnumeratorFactory;
end;
Run Code Online (Sandbox Code Playgroud)

使用这个,我终于可以写:

var L:TMyList;
    E:TMyObject;
begin
  for E in L.FilteredEnum(7) do ;
end;
Run Code Online (Sandbox Code Playgroud)

你知道更好的方法吗?也许Delphi支持直接使用参数调用GetEnumerator的方法?

后来编辑:

我决定使用Robert Love的想法,即使用匿名方法实现枚举器,并使用gabr的"记录"工厂来保存另一个类.这允许我创建一个全新的枚举器,完成代码,在函数中只使用几行代码,不需要新的类声明.

以下是我在库单元中声明我的通用枚举器的方法:

TEnumGenericMoveNext<T> = reference to function: Boolean;
TEnumGenericCurrent<T> = reference to function: T;

TEnumGenericAnonim<T> = class
protected
  FEnumGenericMoveNext:TEnumGenericMoveNext<T>;
  FEnumGenericCurrent:TEnumGenericCurrent<T>;
  function GetCurrent:T;
public
  constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext<T>; EnumGenericCurrent:TEnumGenericCurrent<T>);

  function MoveNext:Boolean;
  property Current:T read GetCurrent;
end;

TGenericAnonEnumFactory<T> = record
public
  FEnumGenericMoveNext:TEnumGenericMoveNext<T>;
  FEnumGenericCurrent:TEnumGenericCurrent<T>;
  constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext<T>;   EnumGenericCurrent:TEnumGenericCurrent<T>);
  function GetEnumerator:TEnumGenericAnonim<T>;
end;
Run Code Online (Sandbox Code Playgroud)

这是一种使用它的方法.在任何类上我都可以添加这样的函数(我有意创建一个不使用a List<T>来展示这个概念的功能的枚举器):

type Form1 = class(TForm)
protected
  function Numbers(From, To:Integer):TGenericAnonEnumFactory<Integer>;  
end;

// This is all that's needed to implement an enumerator!
function Form1.Numbers(From, To:Integer):TGenericAnonEnumFactory<Integer>;
var Current:Integer;
begin
  Current := From - 1;
  Result := TGenericAnonEnumFactory<Integer>.Create(
    // This is the MoveNext implementation
    function :Boolean
    begin
      Inc(Current);
      Result := Current <= To;
    end
    ,
    // This is the GetCurrent implementation
    function :Integer
    begin
      Result := Current;
    end
  );
end;
Run Code Online (Sandbox Code Playgroud)

以下是我如何使用这个新的枚举器:

procedure Form1.Button1Click(Sender: TObject);
var N:Integer;
begin
  for N in Numbers(3,10) do
    Memo1.Lines.Add(IntToStr(N));
end;
Run Code Online (Sandbox Code Playgroud)

ale*_*lex 8

请参阅DeHL(http://code.google.com/p/delphilhlplib/).您可以编写如下代码:

for E in List.Where(...).Distinct.Reversed.Take(10).Select(...)... etc. 
Run Code Online (Sandbox Code Playgroud)

就像你可以在.NET中做的那样(当然没有语法linq).


gab*_*abr 6

你的方法很好.我不知道有什么更好的方法.

枚举器工厂也可以实现为记录而不是接口.

也许你会在这里得到一些想法.


Rob*_*ove 4

Delphi For in 循环支持需要以下之一:(来自文档

  • 编译器识别的基本类型,例如数组、集合或字符串
  • 实现 IEnumerable 的类型
  • 实现《Delphi 语言指南》中记录的 GetEnumerator 模式的类型

如果您查看 Generics.Collections.pas,您会发现它的实现,TDictionary<TKey,TValue>它具有TKeyTValueTPair<TKey,TValue>类型的三个枚举器。Embarcadero 表明他们使用了详细的实现。

你可以这样做:

unit Generics.AnonEnum;
interface
uses
 SysUtils,
 Generics.Defaults,
 Generics.Collections;

type

  TAnonEnumerator<T> = class(TEnumerator<T>)
  protected
    FGetCurrent : TFunc<TAnonEnumerator<T>,T>;
    FMoveNext : TFunc<TAnonEnumerator<T>,Boolean>;
    function DoGetCurrent: T; override;
    function DoMoveNext: Boolean; override;
  public
    Constructor Create(aGetCurrent : TFunc<TAnonEnumerator<T>,T>;
                       aMoveNext : TFunc<TAnonEnumerator<T>,Boolean>);
  end;

  TAnonEnumerable<T> = class(TEnumerable<T>)
  protected
    FGetCurrent : TFunc<TAnonEnumerator<T>,T>;
    FMoveNext : TFunc<TAnonEnumerator<T>,Boolean>;
    function DoGetEnumerator: TEnumerator<T>; override;
  public
    Constructor Create(aGetCurrent : TFunc<TAnonEnumerator<T>,T>;
                       aMoveNext : TFunc<TAnonEnumerator<T>,Boolean>);
  end;

implementation

{ TEnumerable<T> }

constructor TAnonEnumerable<T>.Create(aGetCurrent: TFunc<TAnonEnumerator<T>, T>;
  aMoveNext: TFunc<TAnonEnumerator<T>, Boolean>);
begin
  FGetCurrent := aGetCurrent;
  FMoveNext := aMoveNext;
end;

function TAnonEnumerable<T>.DoGetEnumerator: TEnumerator<T>;
begin
 result := TAnonEnumerator<T>.Create(FGetCurrent,FMoveNext);
end;


{ TAnonEnumerator<T> }

constructor TAnonEnumerator<T>.Create(aGetCurrent: TFunc<TAnonEnumerator<T>, T>;
  aMoveNext: TFunc<TAnonEnumerator<T>, Boolean>);
begin
  FGetCurrent := aGetCurrent;
  FMoveNext := aMoveNext;
end;

function TAnonEnumerator<T>.DoGetCurrent: T;
begin
  result := FGetCurrent(self);
end;

function TAnonEnumerator<T>.DoMoveNext: Boolean;
begin
 result := FMoveNext(Self);
end;

end.
Run Code Online (Sandbox Code Playgroud)

这将允许您匿名声明 Current 和 MoveNext 方法。