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).

  TDemoString = class
  protected
    FText: string;
  public
    constructor Create(const AText:string);
    function GetEnumerator: TDemoEnumerator;
  end;

constructor TDemoString.Create(const AText: string);
begin
  FText := AText;
end;

function TDemoString.GetEnumerator: TDemoEnumerator;
begin
  Result := TDemoEnumerator.Create(Self);
end;

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.

type
  TDemoString = class;

  TDemoEnumerator = record
  private
    FIndex: Integer;
    FDemo : TDemoString;
  public
    constructor Create(demo: TDemoString);
    function GetCurrent: Char; inline;
    function MoveNext: Boolean;
    property Current: Char read GetCurrent;

  end;

{ TDemoEnumerator }

constructor TDemoEnumerator.Create(demo: TDemoString);
begin
  FDemo := demo;
  FIndex := 0;
end;

function TDemoEnumerator.GetCurrent: Char;
begin
  Result := FDemo.FText[FIndex];
end;

function TDemoEnumerator.MoveNext: Boolean;
begin
  Result := FIndex < Length(FDemo.FText);
  if Result then
    Inc(FIndex);
end;

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í.

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

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).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

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.

Poslední komentáře

Comment RSS

Dle měsíců