【问题标题】:Is there a foreach generic method in Delphi for that can be called with anonymous functionDelphi中是否有一个可以用匿名函数调用的foreach泛型方法
【发布时间】:2012-03-27 04:29:03
【问题描述】:

我可以在 C++ 中执行以下操作

for_each(vec->begin(), vec->end(), [](int n){cout << n << " " << endl;});

我想在 Delphi 中做同样/类似的事情。 我之前向 C++ 开发人员询问了question,并希望在 OOP Pascal 中生成类似的示例。 问题的 lambda(匿名方法)部分实际上并不相关,但我想知道是否可以在 Delphi XE2 中做同样的事情。

我问是因为我有 XE,但我不确定它是否已添加。 谢谢。

【问题讨论】:

    标签: delphi generics containers delphi-xe


    【解决方案1】:

    可能与std::vector&lt;T&gt; 最接近的 Delphi 类似物是 TList&lt;T&gt;。您可以使用 for in 循环遍历列表:

    var
      Item: Integer;
      List: TList<Integer>;
    ....
    for Item in List do
      Writeln(Item);
    

    如果您有一个动态数组而不是TList&lt;T&gt;,那么您可以使用for in 来迭代元素。事实上,所有内置容器都支持for in,并且很容易将for in 的支持添加到您自己的类中。

    在 C++ 中没有像 for in 循环这样的东西,所以习惯用法是使用 STL 算法。这就是驱使您使用匿名函数的原因。在 Delphi 中使用 for in 语法,您可以以自然的方式表达“迭代容器的所有成员”,而无需求助于匿名方法。

    在 Delphi 2009 中向 Delphi 添加了泛型,在 Delphi 2005 中添加了 for in 循环,因此所有这些都可以在 XE 中使用。值得一提的是,Delphi 2009 中还添加了匿名功能。

    您必须意识到,Delphi 泛型不如 C++ 模板强大。尽管您谈论的是通用的foreach,但您的代码并不是通用的,因为它专门用于int。您可以用 C++ 编写代码的泛型版本,但由于与模板相比泛型的固有限制,使用 Delphi 泛型更难做到这一点。在 Delphi 中以通用方式编写上述代码的尝试将在您尝试调用 Writeln 时失败。虽然这对于 C++ 模板来说是微不足道的,但对于泛型来说却令人沮丧。

    更新:在 cmets 中,您询问是否有一种巧妙的方法可以将一个容器的内容添加到另一个容器。 AddRange 方法可以做到这一点。 TList&lt;T&gt;.AddRange() 具有三个重载变量,它们接收以下输入参数之一:array of TCollection: IEnumerable&lt;T&gt;Collection: TEnumerable&lt;T&gt;。所有标准的通用容器都遵循类似的模式。

    【讨论】:

    • 我就是这样做的:for i := 0 to n - 1 do result.Add(i);但我想知道这样的通用函数是否存在:)
    • Lambda/Closure/Anonfunction 语法在 delphi 中非常笨拙,以至于 for 循环不需要它。像往常一样,C++ 习语不能很好地翻译成帕斯卡。但是 C# 和 Delphi 确实非常接近。
    • 我发现它在 C++ 中非常有用。不仅代码更干净(对于小型匿名函数),而且在 C++ 中它经常被内联并生成更快的代码。
    • @WarrenP 是的,anon procs 的语法有问题
    • 对不起,我粘贴了错误的代码 :(。我在 Generics.Collections.TList 上进行了迭代:for aInt in aGen do Write(Format('%d', [aInt])) ;
    【解决方案2】:

    这是我基于 Delphi 泛型的单元 Zoomicon.Collections 的摘录:

    uses System.Rtti; //for RttiContext
    
    //...
    
    type
    
      TListEx<T> = class(TList<T>)
        //...
        {ForEach}
        class procedure ForEach(const Enum: TEnumerable<T>; const Proc: TProc<T>; const Predicate: TPredicate<T> = nil); overload;
        procedure ForEach(const Proc: TProc<T>; const Predicate: TPredicate<T> = nil); overload;
      end;
    
    //...
    
    {$region 'ForEach'}
    
    class procedure TListEx<T>.ForEach(const Enum: TEnumerable<T>; const Proc: TProc<T>; const Predicate: TPredicate<T> = nil);
    begin
      if Assigned(Proc) then
        for var item in Enum do
          if (not Assigned(Predicate)) or Predicate(item) then
            Proc(item);
    end;
    
    procedure TListEx<T>.ForEach(const Proc: TProc<T>; const Predicate: TPredicate<T> = nil);
    begin
      {TListEx<T>.}ForEach(self, Proc, Predicate);
    end;
    
    {$endregion}
    

    尝试使用匿名方法,以便您可以捕获上下文并传递给您作为引用传递的 TProc(因为它只接受 T)。以DX、DY为例,见下文:

    type
      Manipulator = class(TFrame)
      pubic
        class procedure MoveControls(const Controls: TControlList; const DX, DY: Single); overload;
        procedure MoveControls(const DX, DY: Single); overload;
      end;
    
    class procedure TManipulator.MoveControls(const Controls: TControlList; const DX, DY: Single);
    begin
      if (DX <> 0) or (DY <> 0) then
        TListEx<TControl>.ForEach(Controls,
          procedure (Control: TControl)
          begin
            with Control.Position do
              Point := Point + TPointF.Create(DX, DY);
          end
        );
    end;
    
    procedure TManipulator.MoveControls(const DX, DY: Single);
    begin
      if (DX <> 0) or (DY <> 0) then
        begin
        BeginUpdate;
        MoveControls(Controls, DX, DY);
        EndUpdate;
        end;
    end;
    
    myManipulator.MoveControls(20, 20);
    

    您可以在那里找到更高级的版本,它们还可以将集合中的项目转换为您需要的类:

    TObjectListEx<TControl>.ForEachClass<TButton>(Controls, SomeProc);
    

    与执行以下操作相比,这是一种优化(因为它不构造中间列表):

    var list := TObjectListEx<TControl>.GetAllClass<TButton>(Controls);
    list.ForEach(SomeProc);
    FreeAndNil(list);
    

    目前在我正在开发的应用程序的存储库中: https://github.com/Zoomicon/READCOM_App/tree/master/Zoomicon.Generics/Collections(未来可能会移至自己的存储库)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 2011-02-18
      • 2018-11-14
      • 2011-05-05
      • 1970-01-01
      • 2021-02-06
      • 2011-12-17
      相关资源
      最近更新 更多