【问题标题】:generic TList of records with a sub list?带有子列表的记录的通用 TList?
【发布时间】:2014-03-06 01:06:10
【问题描述】:

我想在 Delphi XE5 中使用带有子列表的通用 TList 记录:

type
  TMyRecord=record
    Value1: Real;
    SubList: TList<Integer>;
  end;

  TMyListOfRecords=TList<TMyRecord>;

var
  MyListOfRecords: TMyListOfRecords;

无法对记录的字段进行分配:

MyListOfRecords[0].Value1:=2.24; 

MyListOfRecords[0].SubList:=TList<Integer>.Create;

将导致编译器“无法分配左侧”错误。

另见:How to modify TList<record> value?

以下解决方法有效:

AMyRecord:=MyListOfRecords[0];
AMyRecord.Value1:=2.24;
AMyRecord.SubList:=TList<Integer>.Create;
AMyRecord.SubList.Add(33);
MyListOfRecords[0]:=AMyRecord;

由于性能问题,我想避免将数据复制到临时 AMyrecord。我宁愿直接访问记录字段和子列表。

处理这个问题的最佳方法是什么?

【问题讨论】:

    标签: delphi generics tlist


    【解决方案1】:

    列表通过List 属性公开其内部存储,这是一个动态数组。所以你可以写:

    MyListOfRecords.List[0].Value1 := 2.24; 
    

    与具有价值副本的替代方案相比,这是否会在性能上产生任何可衡量的差异,我无法确定。值得检查一下。

    正如@LURD 正确所说,List 返回内部存储。这可能有多个Count 元素。具体来说,它具有Capacity 元素。所以,如果你使用它,你必须使用数组索引访问元素,通过元素0Count-1。还要记住,对列表大小的修改可能涉及重新分配,因此内部存储可能会移动。您对List 的任何引用仅在下一次重新分配之前有效。

    这些警告应建议您仅在性能限制需要时才考虑使用List。即便如此,也要谨慎使用它。

    在我的代码库中,我有一个替代 TList&lt;T&gt; 的方法,它的 Items[] 属性返回一个指向元素的指针。容器仍以动态数组的形式存储,以实现高效的内存布局。我更喜欢这个选项而不是List 属性,因为我觉得它可以让代码更简洁。


    好的,您要求查看我的返回指向元素的指针的列表类。这里是:

    type
      TReferenceList<T> = class(TBaseValueList<T>)
      type
        P = ^T;
      private
        function GetItem(Index: Integer): P;
      public
        property Items[Index: Integer]: P read GetItem; default;
      public
        // .... helper types for enumerators excised
      public
        function GetEnumerator: TEnumerator;
        function Enumerator(Forwards: Boolean): TEnumeratorFactory;
        function ReverseEnumerator: TEnumeratorFactory;
        function IndexedEnumerator: TIndexedEnumeratorFactory;
      end;
    

    现在,需要一些解释。基类TBaseValueList&lt;T&gt;TList&lt;T&gt; 的替代品。如果您愿意,您可以替换 TList&lt;T&gt;。我没有,因为我的基类没有 Items 属性。那是因为我希望专门的课程来介绍它。我的另一个专长是:

    type
      TValueList<T> = class(TBaseValueList<T>)
      private
        function GetItem(Index: Integer): T;
        procedure SetItem(Index: Integer; const Value: T);
      public
        property Items[Index: Integer]: T read GetItem write SetItem; default;
      end;
    

    我的TBaseValueList&lt;T&gt; 的实现非常明显。它与TList&lt;T&gt; 非常相似。我不认为你真的需要看到任何实现。这一切都很明显。

    作为获取元素引用的一种简单方法,您可以像这样将List 包装起来:

    type
      TMyList<T> = class(TList<T>)
      public
        type
          P = ^T;
      private
        function GetRef(Index: Integer): P;
      public
        property Ref[Index: Integer]: P read GetRef;
      end;
    
    function TMyList<T>.GetRef(Index: Integer): P;
    begin
      Result := @List[Index];
    end;
    

    如果您想要一组比 Delphi 提供的更丰富的容器,您可能会关注 Spring4D。虽然我不确定他们是否有像我的容器那样返回引用的东西。

    【讨论】:

    • 请注意,List 属性动态数组可能大于 MyListOfRecords.Count。所以不要将它传递给迭代所有元素的例程。
    • @user3384674 与TList&lt;T&gt; 几乎相同。它没有 Items 属性。这是主要的区别。您可以从TList&lt;T&gt; 中获取代码,并删除ItemsGetItemSetItem,您将非常接近。枚举器也很酷。我不是特别想以可编译的形式显示所有代码。我不确定世界是否还需要另一套容器。你应该能够从我给你的内容中自己遵循这些想法。我希望。您还应该仔细检查性能是否是一个问题。
    • 您甚至可以继承TList&lt;T&gt; 并添加一个属性,该属性使用我描述的List 属性返回对项目的引用。那将是做到这一点的简单方法。甚至可以使用类助手来做到这一点。
    • @user3384674 我做了另一个编辑,展示了一种让访问引用更清晰的简单方法
    • 没有区别。随意使用前者!
    猜你喜欢
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多