代码之家  ›  专栏  ›  技术社区  ›  Cosmin Prund

在TList<TMyObject>

  •  10
  • Cosmin Prund  · 技术社区  · 16 年前

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

    TMyList = TList<TMyObject>
    

    对于此列表,德尔福为我们提供了一个枚举器,因此我们可以这样写:

    var L:TMyList;
        E:TMyObject;
    begin
      for E in L do ;
    end;
    

    var L:TMyList;
        E:TMyObject;
    begin
      for E in L.GetEnumerator('123') do ;
    end;
    

    也就是说,我希望能够使用一些条件为同一个列表提供多个枚举数。不幸的是 for X in Z 需要函数的存在 Z.GetEnumerator

    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;
    

    用这个我终于可以写:

    var L:TMyList;
        E:TMyObject;
    begin
      for E in L.FilteredEnum(7) do ;
    end;
    

    以后编辑:

    我决定使用robertlove的想法,使用匿名方法实现枚举器,并使用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;
    

    这里有一个使用它的方法。在任何类上,我都可以添加这样的函数(我有意创建一个不使用 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;
    

    procedure Form1.Button1Click(Sender: TObject);
    var N:Integer;
    begin
      for N in Numbers(3,10) do
        Memo1.Lines.Add(IntToStr(N));
    end;
    
    5 回复  |  直到 16 年前
        1
  •  4
  •   Robert Love    16 年前

    Delphi For in-loop支持需要以下选项之一:( From the Docs )

    • 识别,如数组、集合或 串
    • I可数
    • 实现 在Delphi语言指南中

    如果您查看Generics.Collections.pas,您会发现 TDictionary<TKey,TValue> 它有三个枚举器 TKey , TValue ,和 TPair<TKey,TValue>

    你可以这样做:

    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.
    

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

        2
  •  8
  •   Oliver Giesen    16 年前

    http://code.google.com/p/delphilhlplib/ ). 您可以编写如下代码:

    for E in List.Where(...).Distinct.Reversed.Take(10).Select(...)... etc. 
    

    就像在.NET中一样(当然没有语法linq)。

        3
  •  6
  •   gabr    16 年前

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

    也许你会有一些想法 here .

        4
  •  3
  •   Nat    16 年前

    GetEnumerator() 函数,如下所示:

    TFilteredEnum = class
    public
      constructor Create(AList:TList<TMyObject>; AFilterValue:Integer);
    
      function GetEnumerator: TFilteredEnum;
    
      function MoveNext:Boolean; // This is where filtering happens
      property Current: TMyObject;
    end;
    

    回归自我:

    function TFilteredEnum.GetEnumerator: TSimpleEnumerator;
    begin
      result := Self;
    end;
    

    Delphi将方便地为您清理实例,就像其他枚举器一样:

    var 
      L: TMyList;
      E: TMyObject;
    begin
      for E in TFilteredEnum.Create(L, 7) do ;
    end;
    

    TFilterFunction = reference to function (AObject: TMyObject): boolean;
    
    TFilteredEnum = class
    private
      FFilterFunction: TFilterFunction;
    public
      constructor Create(AList:TList<TMyObject>; AFilterFunction: TFilterFunction);
    
      ...
    end;
    
    ...
    
    function TFilteredEnum.MoveNext: boolean;
    begin
      if FIndex >= FList.Count then
        Exit(False);
      inc(FIndex);
      while (FIndex < FList.Count) and not FFilterFunction(FList[FIndex]) do
        inc(FIndex);
      result := FIndex < FList.Count;
    end;
    

    这样称呼:

    var 
      L:TMyList;
      E:TMyObject;
    begin
      for E in TFilteredEnum.Create(L, function (AObject: TMyObject): boolean
                                       begin
                                         result := AObject.Value = 7;
                                       end;
                                    ) do 
      begin
        //do stuff here
      end
    end;
    

    那么 你甚至可以让它成为一个通用的,但我不会在这里这样做,我的答案是足够长的,因为它是。

    不@

        5
  •  1
  •   Nige    16 年前

    我使用这种方法…AProc执行过滤测试。

    TForEachDataItemProc = reference to procedure ( ADataItem: TDataItem; var AFinished: boolean );
    
    procedure TDataItems.ForEachDataItem(AProc: TForEachDataItemProc);
    var
      AFinished: Boolean;
      ADataItem: TDataItem;
    begin
      AFinished:= False;
      for ADataItem in FItems.Values do
      begin
        AProc( ADataItem, AFinished );
        if AFinished then
          Break;
      end;
    end;