6

我正在尝试使用for in迭代 a 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可枚举的目标。

如何枚举 a 中的对象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;    

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

4

3 回答 3

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.
于 2014-09-29T15:05:36.990 回答
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>.

于 2014-09-29T16:33:38.977 回答
3

的枚举器在TObjectList中声明TList,它TObjectList是派生自的类。RTL 不会费心为TObjectList. 所以你只剩下TList枚举器了。产生Pointer类型的项目,即持有的项目类型TList

RTL 设计者选择不使用TObjectList. 例如,我提出以下潜在原因:

  1. TObjectListContnrs,就像Generics.Collections. 为什么要花费资源修改过时的类。
  2. 即使TObjectList有一个产生TObject项目的枚举器,你仍然必须投射它们。产生的枚举器TObject真的会更有帮助吗?
  3. 设计者在添加枚举器时只是忘记了这个类的存在。

你该怎么办。一个明显的选择是使用TList<T>TObjectList<T>Generics.Collections. 如果你想坚持使用TObjectList,你可以对它进行子类化并添加一个产生的枚举器TObject。如果您不知道如何操作,可以从文档中了解如何操作。或者您可以使用继承的枚举器并类型转换它产生的指针。

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

于 2014-09-29T15:15:22.800 回答