TList中的TOjectListGenerics.Collections具有.List属性,它是一个枚举器。

例如:

oList := TObjectList<TItem>.Create;
// Add items to oList
for Item in oList.List do begin
  // Do something with Item
end;

这很整洁,但是却产生了严重的后果。 .List只是读取FList(TListTObjectList的私有(private)声明),它只是arrayofT

由于每当添加项目超出其大小时,动态数组的大小都会加倍,这意味着它有空间容纳未使用的项目。

如果您添加了3个TItem,则实际FList的长度为4个项目,第四个(也是最后一个)项目为nil

因此,使用TObjectList.List是不安全的,因为如果您的TObjectList不具有2的幂的.Count值(例如1、2、4、8、16等),则可能会引发访问冲突。

以下代码可能会引发访问冲突:
for Item in oList.List do begin
  Writeln(Item.ClassName);
end;

当然,安全的解决方案是使用.Count进行的简单迭代:
for I := 0 to oList.Count - 1 do begin
  Item := oList.Items[I];
  Writeln(Item.ClassName);
end;

这不像枚举器那么漂亮。 (当然,您也可以检查Item是否为nil。)

因此,我的问题是:
  • 为什么.List不是实际的枚举器?
  • TList/TObjectList是否具有实际的枚举数?

  • 这是TForm的示例(btn1只是添加一行,mmo1TMemo)。
    procedure TForm2.btn1Click(Sender: TObject);
    var
      Line: string;
    begin
      Line := 'Line';
      mmo1.Lines.Add(Line);
      fList.Add(Line);
      mmo1.Lines.Add(Format('Count: %d; Actual length: %d', [fList.Count, Length(fList.List)]));
      for Line in fList.List do begin
        mmo1.Lines.Add(Format('Found: "%s"', [Line]));
      end;
    end;
    

    现在,使用string不会引发访问冲突。但是,当我单击3次后,我得到以下信息:
    Count: 3; Actual length: 4
    Found: "Line"
    Found: "Line"
    Found: "Line"
    Found: ""
    

    最佳答案



    不,不是这样。 List属性是一个动态数组。动态数组内置了对枚举的支持。



    这也不是真的。动态数组不会自动调整大小。必须通过调用SetLength来显式调整它们的大小。 TList<T>类使用对SetLength的调用来管理存储列表内容的基础动态数组的容量。



    我记得,最近在XE3中添加了 List 属性。其目的是允许直接访问基础列表,而这是其他方法所无法实现的。

    有时,为了提高效率,如果您要修改列表的内容,可能会希望避免复印。例如,假设您的列表包含大小为1KB的记录。如果要修改每个记录中的单个 bool 值,而不使用List属性,则最终将整个1KB记录复制两次。仅用于修改一个 bool 值。

    当然,获得对底层存储的访问权的代价是您需要了解内部实现细节。 Embarcadero可能已实现了一些功能,这些功能将以更安全的方式为您提供此访问权限,但无论出于何种原因,他们都会选择此路线。我想我可能已经建立了一个索引属性,该索引属性返回一个指向项目的指针。

    因此,这实际上是List属性的唯一用例。除非您真的需要直接访问基础存储,否则请不要使用List。当然,如果文档烦恼地解释任何一个问题,那会很好,但是确实如此。



    是的,它确实。

    var
      Item: SomeType;
      MyList: TList<SomeType>;
    ....
    for Item in MyList do
      Item.Foo();
    

    10-06 04:55