具有子列表的记录的通用TList?

5

我想在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;

如果这么做,编译器会报“无法分配左侧”错误。

另请参见:如何修改 TList<record> 值?

以下解决方法可行:

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

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

如何处理这个问题最好?

1个回答

6

该列表通过 List 属性公开其内部存储,该存储是一个动态数组。因此,您可以编写以下代码:

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

无论与值复制的替代方案相比是否在性能上有任何可衡量的差异,我无法确定。这值得检查。
正如@LURD所说,List返回内部存储。这可能多于Count个元素。具体来说,它有Capacity个元素。因此,如果您使用它,必须使用数组索引访问元素0到Count-1。还要记住,对列表大小的修改可能涉及重新分配,因此内部存储可能会移动。您对List所做的任何引用仅在下一次重新分配之前有效。
这些警告应提示您仅在性能约束要求时考虑使用List。即使是这样,也要节制使用。
在我的代码库中,我有一个替代TList的选择,其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<T>是我替代TList<T>的选择。如果你愿意,你可以替换为TList<T>。但我不这样做,因为我的基类没有一个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<T>非常明显。它非常类似于TList<T>。我认为您真的不需要看到任何实现细节,因为这都是很明显的。

作为一个获取元素引用的简单方法,您可以像这样包装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。不过我不确定他们是否有类似于返回引用的容器。


@David Heffernan 谢谢!您能否让我们看看您对 TList<T> 的替代方案以及如何使用它?在这个主题上搜索了很多后,我认为这可能会帮助很多人。 - user3384674
1
@user3384674 它与 TList<T> 几乎相同。它没有 Items 属性,这是主要区别。你可以从 TList<T> 中获取代码,并删除 ItemsGetItemSetItem,然后就可以接近了。枚举器也很酷。我不想以可编译的形式展示所有代码。我不确定世界是否需要另一组容器。你应该能够从我给你的内容中自己理解这些想法。希望如此。你还应该仔细检查性能是否是一个问题。 - David Heffernan
1
你甚至可以继承TList<T>并添加一个属性,使用我描述的List属性返回一个项目的引用。那将是简单的方法。或者甚至可以使用类助手来实现。 - David Heffernan
1
@user3384674,我又进行了一次编辑,展示了一种简单的方法来使访问引用更加清晰。 - David Heffernan
1
没有区别。随意使用前者! - David Heffernan
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接