Enumerátory - dar z nebes

vložil Radek Červinka 10. listopadu 2014 22:48

Jedním z důvodů proč už jsem opustil nadobro staré verze Delphi (pod D2007) jsou enumerátory. Toto rozšíření (resp. standard všech moderních jazyků) je pro mne tak významné, že není cesty zpět. Samozřejmě ve spojení s generiky je to teprve jízda, ale i bez nich to jednomu člověku usnadní práci a jsem rád, že i významné komponenty toto podporují. Na konci článku přímo jeden enumerátor vytvoříme.

Na úvod několik ukázek, nejjednodušší ukázka je např. pro práci s řetězci:

var
  c:char;
begin
  for c in sText do
  begin
  //
  end;
end;

TStringList

var
  list:TStringList;
  s: string;
begin
  list :=TStringList.Create;
  list.CommaText := 'r,a,d';
  for s in list do
    ListBox1.Items.Add(s);
  list.Free;
end;

Nebo VirtualTreeView jako ukázka komponent třetí strany - no není to paráda:

var
  pNode: PVirtualNode;
begin
   for pNode in VirtualStringTree1.CheckedNodes do
   begin
//     ..
   end;
end;

S generiky

var
  list:TList<string>;
  s: string;
begin
  list :=TList<string>.Create;
  list.Add('prvni');
  list.Add('druhy');

  for s in list do
    ListBox1.Items.Add(s);
  list.Free;
end;

Někdy nastane ta chvíle, kdy vytvoříte vlastní třídu a chcete přidat enumerátor. Postup ukáži na ukázce. Mějme třídu, která obsahuje string a enumerátor bude vracet pro jednoduchost jednotlivé znaky (tj. komplikovaná varianta prvního příkladu).

    1TDemoString = class
    2  protected
    3    FText: string;
    4  public
    5    constructor Create(const AText:string);
    6    function GetEnumerator: TDemoEnumerator;
    7  end;
    8
    9constructor TDemoString.Create(const AText: string);
   10begin
   11  FText := AText;
   12end;
   13
   14function TDemoString.GetEnumerator: TDemoEnumerator;
   15begin
   16  Result := TDemoEnumerator.Create(Self);
   17end;

Klíčové je GetEnumerator, což je metoda, která vrátí instanci enumerátoru. Ideálně by měla třída implementovat rozhraní IEnumerable, ale není to nutné, stačí public metoda GetEnumerator.

Nyní vytvoříme enumerátor, v souboru je před deklarací třídy, protože třída se na něho odkazuje.

    1type
    2  TDemoString = class;
    3
    4  TDemoEnumerator = record
    5  private
    6    FIndex: Integer;
    7    FDemo : TDemoString;
    8  public
    9    constructor Create(demo: TDemoString);
   10    function GetCurrent: Char; inline;
   11    function MoveNext: Boolean;
   12    property Current: Char read GetCurrent;
   13
   14  end;
   15
   16{ TDemoEnumerator }
   17
   18constructor TDemoEnumerator.Create(demo: TDemoString);
   19begin
   20  FDemo := demo;
   21  FIndex := 0;
   22end;
   23
   24function TDemoEnumerator.GetCurrent: Char;
   25begin
   26  Result := FDemo.FText[FIndex];
   27end;
   28
   29function TDemoEnumerator.MoveNext: Boolean;
   30begin
   31  Result := FIndex < Length(FDemo.FText);
   32  if Result then
   33    Inc(FIndex);
   34end;

Enumerátor musí implementovat nejméně MoveNext, GetCurrent. První metoda se posouvá na další záznam, druhá vrací aktuální záznam. Myslím, že implementace mého enumerátoru je velmi přímočará.

Poslední věc, spíše zajímavost je následující fígl: Uvedený VirtualTreeView má enumerátor deklarován následovně.

  TVTVirtualNodeEnumerator = {$ifdef COMPILER_10_UP}record{$else}class{$endif}
  private

Předpokládám, že je to z hlediska optimalizace, jelikož záznamy jsou vytvářeny na zásobníku, resp. třída na haldě, tj. záznamy budou efektivnější - není třeba alokace.

Rád si přečtu Vaše připomínky a vylepšení.


Nabízíme Delphi školení a konzultace na různá témata, primárně ve Vaší firmě.

Tagy:

Delphi | Praxe

Komentáře

11.11.2014 8:40:20 #

Vladimír Klaus

Téměř vždy, když se po čase rozhodnu tuto "modernu" použít, se objeví potřeba znát index, takže to zase změním zpět na klasický for i. Proto to z preventivních důvodů už nedělám :-). Ale jinak prima vylepšení, zlepšuje čitelnost.

Pak by ale potěšila ještě deklarace (+automatické uvolnění) ve stylu:
   foreach(var row in data) { ... }

Vladimír Klaus

11.11.2014 16:28:55 #

Ondřej Pokorný

Vladimír Klaus: přesně ten problém s indexy mám taky :) Jen v minimu případů ho není nutné znát :(

Ondřej Pokorný

11.11.2014 16:50:11 #

radekc

No já jsem si to třeba u VirtualTreeView myslel taky, než jsem objevil CheckedNodes a další enumerátory. Náhle se potřeba rapidně snížila - ale v určitých případech to je pravda - resp. možná spíše zvyk.

radekc

11.11.2014 17:56:38 #

Daniel Andrascik

Enumenatory pokladam za uzitocne hlavne z dvoch dovodov. Su o nieco bezpecnejsie, mensia sanca ze koli nejakej chybke sa budem snazit niekde nieco citat alebo v horsom pripade zapisovat mimo rozsah (pola, listu, stringu atd...). A za druhe je to asi jedina moznost ako prechadzat polozky objektu TDictionaries<Key,Value> (aspon teda ja som inu moznost neobjavil)

Daniel Andrascik

Komentování ukončeno

Naše nabídka

Partial English version.

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Anketa

Poslední komentáře

Comment RSS