Pętla "for .. in" na TObjectList - jak to zapisać?

0

Powiedzcie mi proszę, czy pętlę:

for i := 0 to TObjectList.Count -1 do
  Object := TObjectList.Items[i];

mogę zastąpić pętlą for-in, coś w stylu

 for Object in TObjectList.???? do ... 

?

przykład definicji klasy tutaj: http://pastebin.com/Ld8N50zj

3

Nie wiem o co ci chodzi dlaczego TObjectList bo to raczej typ a nie utworzona lista obiektów ale weźmy np.:

type
 TMyObj= class(TObject) //taki prosty obiekt
  private
    fNo: Integer;
  public
     property No: Integer read fNo write fNo;
  end;   
//....
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  ObjectList: TObjectList<TMyObj>;
  MyObj: TMyObj;
begin
  ObjectList:= TObjectList<TMyObj>.Create;
  ObjectList.OwnsObjects:= True;

  for i:= 0 to 2 do  //dodac z 3 elementy do listy
  begin
    MyObj:= TMyObj.Create;
    MyObj.No:= i;
    ObjectList.Add(MyObj);
  end;

  for i := 0 to ObjectList.Count -1 do
  begin
    MyObj:= ObjectList.Items[i];
    ShowMessage(IntToStr(MyObj.No));
  end;

  for MyObj in ObjectList do
    ShowMessage(IntToStr(MyObj.No));


  ObjectList.Free;
end;

To te 2 ostatnie czyli odczytujące robią to samo.

0
kAzek napisał(a):

Nie wiem o co ci chodzi dlaczego TObjectList bo to raczej typ a nie utworzona lista obiektów

czy to oznacza, że definiując swoją klasę dziedziczącą po TObject bądź nie, a następnie ileś jej obiektów to nie muszę definiować również ObjectList do przechowywania tychże obiektów? Jest jakiś inny, prostszy sposób poza oczywiście zmiennymi?

zrobiłem w ten sposób:

var
  tempOsoba:TOsoba;
begin
  for tempOsoba in fmMain.listaOsob do { fmMain to formularz główny, a listaOsob obiekt klasy dziedziczącej po TObjectList }
      begin
        showMessage(tempOsoba.Nazwisko);
      end;

i kompilator zwraca: [dcc32 Error] raport.pas(120): E2010 Incompatible types: 'TOsoba' and 'Pointer'

3

Chyba problem polega na tym że twoja lista obiektów powinna być typu TObjectList<TOsoba> tak jak w przykładzie który podałem wyżej (nie musisz się bawić w jakieś TOsobaList po prostu to znacznie ułatwia życie choć jak chcesz to TOsobaList<TOsoba> też chyba może być ważne aby kompilator "wiedział" że ma do czynienia z listą obiektów typu TOsoba). Tylko nie wiem jaką masz wersję Delphi bo aby można było coś takiego zrobić muszą być obsługiwane typy generyczne które zostały wprowadzone dopiero w wersji 2009. Oczywiście uses trzeba dodać Generics.Collections

0

Mam XE3, więc problemu być nie powinno, typy generyczne obsługuje :)
Przyznam, że nie rozumiem zapisu "TOsobaList<TOsoba>". Chodzi o dziedziczenie TOsobaList po TOsoba? To nie problem, ale jak wtedy przechowywać obiekty w TOsobaList skoro nie będę dziedziczył po TObjectList? Mam sam dodać metody Add, Delete, wlasności Items?
Poza tym czy jeśli definicja TOsobaList wygląda tak:

type
 type TOsobaList = class(TObjectList)
  private
    function GetItem(AIndex: integer): TOsoba;
    procedure SetItem(AIndex: integer; const Value: TOsoba);
  public
    constructor Create(AWlascicielObiektow: Boolean);
    destructor Destroy; override;
    property Items[AIndex: integer]: TOsoba read GetItem write SetItem; default;
    function Add(AOsoba: TOsoba): integer;
    function FindItem(NazwiskoImie: string): integer;
  end;

to czy kompilator nie wie, że itemy tutaj są typu TOsoba? Widać to szczególnie po parametrze funkcji Add, oraz typie własności Item. Trochę już zgaduję, ale na logikę...

3

Przyznam, że nie rozumiem zapisu "TOsobaList<TOsoba>".

No to chyba czas najwyższy nieco na temat typów generycznych poczytać, zanim zacznie się je wykorzystywać;

Zapis ten oznacza, że obiekt klasy TOsobaList<TOsoba> będzie listą obiektów klasy TOsoba, tak samo jak stara wersja TObjectList jest listą obiektów klasy TObject; Generyki dają możliwość stworzenia listy obiektów dowolnej klasy, bez konieczności rzutowania na dany typ czy korzystania z operatorów Is/As.

0

Wstyd, że ja pierdziele :D Z XE3 korzystam od stosunkowo niedawno, w d7 generyków nie było. Dzięki za wyjaśnienia.

EDIT:
@furious programming: mam jeszcze jedno pytanie, a google odpowiedzi nie znają. Chcąc stosować typy generyczne muszę dodać do uses bibliotekę System.Generics - nawet środowisko podpowiada takową podczas pisania. Ale dlaczego podczas kompilacji wywala błąd Not Found System.Generics.dcu? Generics.dcu analogicznie.

EIDT:

poszło z Generics.Collections, sorry za niepotrzebne wołanie :)

0

@Furious Programming: sorry, że odświeżam wątek, ale mam jeszcze jedno pytanie odnośnie typów generycznych, na które odpowiedzi nie ma na googlach. W przypadku definiowania własnej listy obiektów dziedziczącej po TListObject mogłem napisać funkcję wyszukującą dany TObject. Przykład poniżej:

function TOsobaList.FindItem(ANazwisko:string):integer;
var
  i:integer:
begin
  result := -1;
  for i :=  0 to self.count -1 do
    if SameText(self.items[i].Nazwisko, ANazwisko) then
    begin
      result := self.items[i].ID;
      break;
    end;
end;

i wtedy był porządek w kodzie. Stosując generyki też mogę użyć podobnego rozwiązania? Z tego co widzę za każdym razem podczas próby zwrócenia np ID człowieka po jego nazwisku muszę robić w kodzie całą pętlę:

...
listaOsob:TOsobaList<TOsoba>
...
for tempOsoba in listaOsob do
  if tempOsoba.Nazwisko = ANazwisko ...
...

czy mam rację? Jeśli chciałbym zrobić to w sposób "koszerny" znów musiałbym zdefiniować własny typ dziedziczący po generyku z metodą FindItem? A to mija się z celem jak na moją logiką skoro są listy obiektów TObjectList :D Wytłumacz mi bardzo proszę.

0

@tomix - a nie możesz po prostu skorzystać z metody FindItem:

{...}
listaOsob: TOsobaList<TOsoba>;
{...}

intIndex := listaOsob.FindItem('Kowalski');

bez korzystania z pętli?

Swoją drogą w metodzie FindItem też możesz wykorzystać pętlę for in:

function TOsobaList.FindItem(ANazwisko: String): Integer;
var
  Osoba: TOsoba;
begin
  Result := -1;

  for Osoba in Items do
    if SameText(Osoba.Nazwisko, ANazwisko) then
    begin
      Result := Osoba.ID;
      Exit;
    end;
end;

lub krótszą wersję, jeśli Twoje Delphi posiada rozbudowaną wersję instrukcji Exit pozwalającej przyjmować parametry (jak w Lazarusie):

function TOsobaList.FindItem(ANazwisko: String): Integer;
var
  Osoba: TOsoba;
begin
  Result := -1;

  for Osoba in Items do
    if SameText(Osoba.Nazwisko, ANazwisko) then
      Exit(Osoba.ID);
end;

To tylko w ramach ciekawostki, jeśli pole Items (lub właściwość, bo pole powinno być FItems) jest listą obiektów klasy TOsoba.

0

@Furious Programming: wprowadziłem Cię zapisem w błąd, miało to wyglądać tak:

var
listaOsob : TObjectList<TOsoba>;
begin
listaOsob := TObjectList<TOsoba>.Create(True);
end;

i teraz żebyś mnie dobrze zrozumiał - klasa TObjectList nie ma metody FindItem, a żeby ją napisać muszę zdefiniować nową klasę dziedziczącą po TObjectList - tak? Myślałem, że generyki oferują w sobie aż tyle, że nie będę ich musiał dziedziczyć i dostosowywać pod siebie (w sensie tworzyć list obiektów jak do tej pory). Jeśli się nie mylę, to typy generyczne muszę stosować tak samo jak w przypadku poniższym aby spełniały moje wymagania co z kolei mija się z celem przerabiania poniższego rozwiązania na generyczne:

 type TOsobaList = class(TObjectList)
  private
    function GetItem(AIndex: integer): TOsoba;
    procedure SetItem(AIndex: integer; const Value: TOsoba);
  public
    constructor Create(AWlascicielObiektow: Boolean);
    destructor Destroy; override;
    property Items[AIndex: integer]: TOsoba read GetItem write SetItem; default;
    function Add(AOsoba: TOsoba): integer;
    function FindItem(NazwiskoImie: string): integer;
  end;
0

Zwróć uwagę, że Twoja klasa TOsobaList nie jest klasą generyczną.

0

@Furious Programming:
Oczywiście masz rację, z tego co wyczytałem zmiana jest kuriozalna w deklaracji:

 type TOsobaList = class(TObjectList) // zwykła lista obiektów
 type TOsobaList = class<TObjectList> // lista generyczna

ale czym one się różnią prócz nomenklaturą i możliwością stosowania pętli for .. in ? Bo widzę, że tą drugą i tak trzeba po swojemu zdefiniować żeby spełniała takie same wymagania jak pierwsze.

0

@tomix - moja wiedza na temat generyków jest szczątkowa, bo jakoś nie miałem okazji się ich nauczyć i wykorzystać, ale z tego (powyższego) zapisu wynika na zdrowy rozsądek, że:

type TOsobaList = class(TObjectList)

jest klasą dziedziczącą po TObjectList (więc ten zapis jest błędny, bo nowa klasa dziedziczy po samej sobie); Natomiast:

type TOsobaList = class<TObjectList>

jest generyczną klasą listy, która zawiera itemy klasy TObjectList, czyli każdy jej element także będzie listą.

0

A czemu nie?

  TOsobaList = class(TObjectList<TOsoba>)
  public
    function FindItem(NazwiskoImie: string): Integer;
  end;
0

@kAzek: nie wpadłem na to, jest to jakieś rozwiązanie.
Może przy okazji powiesz mi, czy jest faktycznie sens tak kombinować z generykami, czy zostać przy rozwiązaniu z D7 i TListObject? Jakie profity w porównaniu do listy obiektów niosą generyki prócz pętli for .. in ? :)

1

Praktycznie żadnych wszystko sprowadza się do tego że skracasz kod. Chodzi właśnie o takie udogodnienia że nie musisz definiować własnej metody Items itd. bo kompilator i tak wie że Item ma być typu TOsoba.

0

Jakie profity w porównaniu do listy obiektów niosą generyki prócz pętli for .. in ?

Ta pętla nie jest wykorzystywana jedynie przy listach generycznych, można ją wykorzsytywać np. przy macierzach:

const
  arrNumbers: array [0 .. 4] of UInt32 = (7, 12, 56, 2, 30);
var
  intNumber: UInt32;
begin
  for intNumber in arrNumbers do
    WriteLn(intNumber);

przy zbiorach:

type
  TEnum = (enumFirst, enumSecond, enumThird, enumFourth);
  TEnums = set of TEnum;
const
  EnumsSet: TEnums = [enumFirst, enumSecond, enumFourth];
var
  enumItem: TEnum;
begin
  for enumItem in EnumsSet do
    WriteLn(enumItem);

czy nawet w klasach niegenerycznych (przynajmniej pod Lazarusem):

var
  slItems: TStrings;
  strItem: AnsiString;
begin
  slItems := TStringList.Create();
  try
    slItems.Add('first');
    slItems.Add('second');
    slItems.Add('third');

    for strItem in slItems do
      WriteLn(strItem);
  finally
    slItems.Free();
  end;

Tak że pętla for in ma wiele zastosowań, nie tylko związanych z generykami.

0

Wszystko rozumiem. Dziękuję Panowie za obszerne wyjaśnienia :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1