如何在TObjectList中做?

时间:2014-09-29 14:59:03

标签: delphi for-loop iterator for-in-loop listiterator

我正在尝试使用for in来迭代TObjectList

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Contnrs;

var
    list: TObjectlist;
    o: TObject;
begin
    list := TObjectList.Create;
    for o in list do
    begin
        //nothing
    end;
end.

无法编译:

  

[dcc32错误] Project1.dpr(15):E2010不兼容的类型:'TObject'和'Pointer'

似乎Delphi的for in构造不能处理无类型的,未说明的,TObjectList和可枚举的目标。

如何枚举TObjectList中的对象?

我现在做什么

我目前的代码是:

procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
   i: Integer;
   o: TObject;
begin
   for i := 0 to BatchList.Count-1 do
   begin
      o := BatchList.Items[i];

      //...snip...where we do something with (o as TCustomer)
   end;
end;    

没有充分的理由,我希望将其改为:

procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
   o: TObject;
begin
   for o in BatchList do
   begin
      //...snip...where we do something with (o as TCustomer)
   end;
end;    

为什么要使用枚举器?只是因为。

3 个答案:

答案 0 :(得分:5)

使用泛型你可以有一个打字的对象列表(只是注意到了评论,但无论如何都不好完成)

如果您正在寻找使用TObjectList<T>的充分理由,原因在于它可以为您节省大量代码

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Generics.Collections;
Type
  TCustomer = class
  private
  public
    //Properties and stuff
  end;

var
    list: TObjectlist<TCustomer>;
    c: TCustomer;
begin
    list := TObjectList<TCustomer>.Create;
    for c in list do
    begin
        //nothing
    end;
end.

答案 1 :(得分:4)

  

如何枚举 TObjectList

中的对象

要回答具体问题,这是引入枚举器的一个示例。 请注意,您必须创建TObjectList的后代才能添加GetEnumerator函数。您可以不使用类助手进行子类化,但我将其留作感兴趣的读者的练习。

type

TObjectListEnumerator = record
private
  FIndex: Integer;
  FList: TObjectList;
public
  constructor Create(AList: TObjectList);
  function GetCurrent: TObject;
  function MoveNext: Boolean;
  property Current: TObject read GetCurrent;
end;

constructor TObjectListEnumerator.Create(AList: TObjectList);
begin
  FIndex := -1;
  FList := AList;
end;

function TObjectListEnumerator.GetCurrent;
begin
  Result := FList[FIndex];
end;

function TObjectListEnumerator.MoveNext: Boolean;
begin
  Result := FIndex < FList.Count - 1;
  if Result then
    Inc(FIndex);
end;

//-- Your new subclassed TObjectList

Type

TMyObjectList = class(TObjectList)
  public
    function GetEnumerator: TObjectListEnumerator;
end;

function TMyObjectList.GetEnumerator: TObjectListEnumerator;
begin
  Result := TObjectListEnumerator.Create(Self);
end;

枚举器的这种实现使用记录而不是类。这样做的好处是在执行for..in枚举时不会在堆上分配额外的对象。

procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
   o: TObject;
begin
   for o in TMyObjectList(BatchList) do // A simple cast is enough in this example
   begin
      //...snip...where we do something with (o as TCustomer)
   end;
end;   

正如其他人所说,有一个泛型类是更好的选择,TObjectList<T>

答案 2 :(得分:3)

TObjectList的枚举器在TList中声明,TObjectList是派生TObjectList的类。 RTL不打算为TList声明更具体的枚举器。所以你留下了Pointer枚举器。这会产生TList类型的项目,这是TObjectList所持有的项目类型。

RTL设计师选择不对TObjectList做任何事情可能有几个原因。例如,我提出以下潜在原因:

  1. Contnrs,与Generics.Collections中的所有内容一样,已被TObjectList中的通用容器弃用并过时。为什么要花资源修改过时的类。
  2. 即使TObject有一个枚举器产生TObject个项目,您仍然需要投射它们。屈服TList<T>的普查员真的会更有帮助吗?
  3. 设计师只是忘记了这个类在添加枚举器时存在。
  4. 你应该怎么做。一个明显的选择是使用TObjectList<T>中的Generics.CollectionsTObjectList。如果您希望保留TObject,则可以对其进行子类化并添加一个产生{{1}}的枚举数。如果您不知道如何操作,可以从documentation了解如何执行此操作。或者你可以使用继承的枚举器并输入它所产生的指针。

    在我看来,因为您准备将索引从索引for循环修改为for循环,这意味着您已准备好对代码进行非平凡的更改。在这种情况下,通用容器似乎是显而易见的选择。