【问题标题】:How can I pass a group of objects to a function for creation?如何将一组对象传递给一个函数进行创建?
【发布时间】:2011-01-05 19:58:29
【问题描述】:

所以我在 Delphi 2007 工作,我正在清理我的代码。我注意到在很多过程中我声明了许多相同类型的不同变量。

例如,我现在正在查看的一个过程我声明了 4 个不同的字符串列表,我必须为每个列表键入 var1 := TStringList.Create

我的想法是创建一个程序,该程序接收一个开放的变量数组、我的 4 个变量列表,然后将它们全部创建。电话会是这样的

CreateStringLists([var1,var2,var3,var4]);

但据我所知,您不能通过引用传递打开的数组,因此不能做我希望做的事情。有人对此有什么有趣的想法吗?

【问题讨论】:

    标签: delphi delphi-2007 variable-initialization open-array-parameters


    【解决方案1】:

    在重构过程中,您通常需要对代码有一个非常的视野。为什么要“清理”一些这样的操作,而您很可能根本不应该执行这些操作?

    在这种情况下,我怀疑您有一个例程需要处理 4 个单独的字符串列表。这似乎不太可能具有良好的凝聚力。也许相反,它应该是一个调用四次的字符串列表处理例程。所以我真的很想看看整个例程,而不是评论如何让这个细节更漂亮。

    【讨论】:

    • 我正在开发一些简单的演示软件,这特别涉及幻灯片(power point esq)演示。在每张幻灯片上,我都有 3 个不同的文本区域:正文、标题、版权。然后是另一个字符串列表,它处理从文本文件中解析文本,即幻灯片如何保存到磁盘。它们都是为了一个独特的目的而创建的。我很喜欢你的回答……我会考虑的。
    【解决方案2】:

    您可以使用 Delphi 做任何事情(或几乎任何事情)。我不推荐使用下面的代码,只是为了知道这个技巧是可能的:

    type
      PStringList = ^TStringList;
    
    procedure CreateStringLists(const SL: array of PStringList);
    var
      I: Integer;
    
    begin
      for I:= 0 to High(SL) do begin
        SL[I]^:= TStringList.Create;
      end;
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    var
      SL1, SL2, SL3: TStringList;
    
    begin
      CreateStringLists([@SL1, @SL2, @SL3]);
      SL3.Add('123');
      Caption:= SL3[0];
      SL1.Free;
      SL2.Free;
      SL3.Free;
    end;
    

    【讨论】:

    • +1 我怀疑自己能不能做到这一点,但它是类型安全的,而且相当隐蔽——太好了!
    • +1 你打败了我!我现在有自己的基于普通指针的实现,当我注意到你的答案时,我只是升级它以使用指向 TStringList 的指针。 :)
    • 我认为,当然,您必须在 CreateStringLists 过程中使其异常感知,以便成功创建所有这些,或者在以异常方式退出例程之前释放所有创建的。
    • @jachguate - 代码只是说明。如果您将在实际项目中使用它,可以对其进行改进。例如,可以在调用构造函数之前将指针设为 nill,将过程替换为返回创建的对象数量的函数,否则。
    • @Serg:在像 SO 这样的公共场所,我赞成编写健壮的插图代码,或者至少指出以描述性方式使其健壮的方法。看看@David Heffernan 对这个问题的回答。
    【解决方案3】:

    其实4个构造函数有什么问题?

    【讨论】:

    • 实际上,这看起来像是评论,而不是真正的答案。
    • 功能上什么都没有,视觉上它会使我要查看的代码更短并且(对我而言)更易于查看。
    【解决方案4】:

    如果在您的上下文中有意义,您可以在专门的 TObjectList 中聚合声明。

    type
      TMyList<T:class,constructor> = class(TObjectList<T>)
      public
        procedure CreateItems(const ACount : integer);
      end;
    
    procedure TMyList<T>.CreateItems(const ACount: integer);
    var
      Index: Integer;
    begin
      for Index := 0 to (ACount - 1) do Add(T.Create);
    end;
    
    // Test procedure
    procedure TestMe;
    var
      MyStringsList : TMyList<TStringList>;
    begin
      MyStringsList := TMyList<TStringList>.Create(True);
      MyStringsList.CreateItems(10);
      // ...
      FreeAndNil(MyStringsList);
    end;
    

    所以你可以专门化你的列表。

    【讨论】:

      【解决方案5】:

      您可以使用 2、3、4 等参数创建一系列重载版本。例如:

      procedure CreateStringLists(var L1, L2: TStringList); overload;
      procedure CreateStringLists(var L1, L2, L3: TStringList); overload;
      procedure CreateStringLists(var L1, L2, L3, L4: TStringList); overload;
      
      procedure CreateStringLists(var L1, L2: TStringList);
      begin
        L1 := nil;
        L2 := nil;
        Try
          L1 := TStringList.Create;
          L2 := TStringList.Create;
        Except
          FreeAndNil(L2);
          FreeAndNil(L1);
          raise;
        End;
      end;
      
      // etc.
      

      如果我这样做,我会编写一个脚本来生成代码。

      顺便说一句,在我自己的代码中,我会在该函数的开头写InitialiseNil(L1, L2),在异常处理程序中写FreeAndNil(L2, L1)InitialiseNilFreeAndNil 是由一个非常简单的 Python 脚本生成的函数,该脚本作为注释包含在代码库中,以便可以重新运行。像上面定义的CreareStringLists 这样的例程只有在你有一个匹配的例程来一次性释放它们时才有用。这允许您编写:

      CreateStringLists(L1, L2);
      Try
        // do stuff with L1, L2
      Finally
        FreeAndNil(L2, L1);
      End;
      

      最后,我并不是说我一定会这样做,但这是对这个问题的天真而直接的回答。正如@T.E.D.状态,这样做的需要表明代码库中存在更深层次的问题。

      【讨论】:

      • +1 我会将它与 Serg 的解决方案结合起来:让他的一个私有的,并公开你的代码,因为它更干净。
      猜你喜欢
      • 2021-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-03
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多