TList
中的TOjectList
和Generics.Collections
具有.List
属性,它是一个枚举器。
例如:
oList := TObjectList<TItem>.Create;
// Add items to oList
for Item in oList.List do begin
// Do something with Item
end;
这很整洁,但是却产生了严重的后果。
.List
只是读取FList
(TList
和TObjectList
的私有(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
只是添加一行,mmo1
是TMemo
)。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();