2014-09-29 7 views
6

Ich versuche for in zu verwenden, um eine TObjectList iterieren:Wie geht es 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. 

Und es nicht kompilieren:

[DCC32 Error] Project1.dpr (15): Inkompatible Typen E2010: 'TObject' und ‚Pointer‘

Es scheint, als ob Delphi for in Konstrukt behandelt nicht die untypisierten, undescended, TObjectList ein als zählbare Ziel.

Wie liste ich die Objekte in einem TObjectList auf?

Was ich jetzt tun

Mein aktueller Code ist:

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;  

für keinen guten Grund, ich hatte gehofft, es zu ändern:

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;  

Warum einen Enumerator verwenden? Gerechte Sache.

+3

Verwenden Sie generische 'TObjectList ' anstelle von 'TObjectList'. – whosrdaddy

+1

Beispiel auf die harte Tour: [Unterstützung für In-Schleife in TObjectList-Nachkommen] (http://www.remkoweijnen.nl/blog/2008/03/02/supporting-for-in-loop-in-tobjectlist-descendants/). –

+1

Ich habe dieses Problem im Jahr 2005 als [Bug 10790] (http://qc.embarcadero.com/wc/qcmain.aspx?d=10790) gemeldet. Im Jahr 2011 wurde es "auf die nächste Version verschoben". –

Antwort

4

Wie liste ich die Objekte in einer TObjectList auf?

Um die spezifische Frage zu beantworten, ist dies ein Beispiel für die Einführung eines Enumerators. Beachten Sie, dass Sie einen Nachfolger von TObjectList erstellen müssen, um die GetEnumerator-Funktion hinzuzufügen. Sie könnten ohne Unterklassen mit einem Klassenhelfer auskommen, aber ich überlasse das als Übung für den interessierten Leser.

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; 

Diese Implementierung eines Enumerators verwendet einen Datensatz anstelle einer Klasse. Dies hat den Vorteil, dass beim Aufrufen von for..in Aufzählungen kein zusätzliches Objekt auf dem Heap zugewiesen wird.

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; 

Wie andere bereits festgestellt haben, gibt es eine Generika-Klasse, die eine bessere Option ist, verwenden TObjectList<T>.

+0

Dies beantwortet die Frage. Obwohl es offensichtlich ist, dass die Antwort ist: einfach weiterhin den Ganzzahl-Index verwenden. Ich versuche nur das "for-in" zum Spaß zu benutzen; es fügt nichts wirklich hinzu. –

5

Generika verwenden Sie eine typisierte Object haben kann (habe gerade bemerkt, den Kommentar, aber schlecht Finish dies ohnehin)

Und wenn Sie suchen einen guten Grund, einen TObjectList<T>, dass Grund zu verwenden, wird sein, dass es spart Ihnen eine viele Type-Casting im Code

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

Sie meinen 'list: = TObjectList .Create' –

+0

Leider bietet die API eine' TObjectList'. –

+0

@DavidHeffernan oops mein schlechtes. Ja, das ist, was ich meinte –

3

der Enumerator für TObjectList in TList deklariert wird, die die Klasse ist, von dem TObjectList abgeleitet ist. Die RTL kümmert sich nicht darum, einen spezifischeren Enumerator für TObjectList zu deklarieren. So bleiben Sie mit dem TList Enumerator. Das ergibt Artikel des Typs Pointer, der von TList gehalten wird.

Es gibt wahrscheinlich ein paar Gründe, warum die RTL-Entwickler entschieden haben, nichts mit TObjectList zu tun. Zum Beispiel postulieren ich folgende mögliche Gründe:

  1. TObjectList, wie alles in Contnrs veraltet und wurde durch den generischen Container in Generics.Collections obsolet. Warum sollte die Ressource eine veraltete Klasse ändern?
  2. Auch wenn TObjectList einen Enumerator hatte, der TObject Elemente ergab, müssten Sie sie dennoch umsetzen. Würde ein Enumerator, der TObject ergab, wirklich so hilfreicher sein?
  3. Die Designer haben einfach vergessen, dass diese Klasse existierte, als sie Enumeratoren hinzufügten.

Was sollten Sie tun. Eine naheliegende Wahl ist die Verwendung von TList<T> oder TObjectList<T> von Generics.Collections. Wenn Sie mit TObjectList fortfahren möchten, können Sie es unterteilen und einen Enumerator hinzufügen, der TObject ergab. Wenn Sie nicht wissen, wie das geht, können Sie herausfinden, wie Sie das tun, von der documentation.Oder Sie könnten den geerbten Enumerator verwenden und die übergebenen Zeiger typisieren.

Es scheint mir, dass Sie bereit sind, den Code von einer indizierten for-Schleife zu einer for-in-Schleife zu ändern, was bedeutet, dass Sie bereit sind, nicht-triviale Änderungen am Code vorzunehmen. In diesem Fall scheint der generische Container die offensichtliche Wahl zu sein.

+0

Vielleicht erfordert das OP die Verwendung von für in-Schleife statt oder regelmäßig für Schleife, weil er die Fähigkeit des Entfernens einiger Objekte in TObjectList innerhalb dieser Schleife benötigt. Wenn dies der Fall ist, wäre der einfachste Weg, die while-Schleife anstelle der for-Schleife zu verwenden. Infact while-Schleife ist das, was in den Enumeratoren verwendet wird. – SilverWarior

+0

@SilverWarior Sie können die Liste in einer for in-Schleife nicht mehr umkehren als in einer klassischen indizierten for-Schleife. Ian möchte eine For-In-Schleife verwenden, weil sie viel besser lesbar ist. –

+0

@David, in Bezug auf Argument 2: Es gibt immer noch einen Unterschied zwischen 'x: = TObject (o) als TMyClass' und' x: = o als TMyClass', IMO. –