Delphi už dlouho má procedurální typy (tj. typu procedura nebo funkce, v podstatě pointer) a ukazatele na metody (
tj. typ pointer na metodu). To druhé je základem celé VCL - kdykoliv deklarujete třeba OnClick obsluhu jedná se právě
o druhý případ. První případ je používaný méně, ale třeba já ho často používám pro různé callback funkce atd.
Anonymní metody rozšiřují stávající stav, kdy místo jména procedury nebo metody prostě napíšete kód. Navíc se v takovém případě mění
platnost lokálních proměnných.
Pro připomenutí deklarace:
type
TLogProc = procedure (const sText:string); // první případ
TLogMethod = procedure (const sText:string) of object; // druhý případ
TAnonProc = reference to procedure (const sText:string); // anonymní metoda
Jednoduchý neužitečný příklad
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, TypInfo;
type
TAnonProc = reference to procedure (const sText:string); // anonymní metoda
procedure Test;
var
pTest: TAnonProc;
begin
pTest :=
procedure (const s: string)
begin
writeln(s);
end;
pTest ('To je teprve zacatek');
pTest ('A znovu');
pTest ('Popojedem');
end;
procedure Test2(p:TAnonProc);
begin
p('radek 1');
p('radek 2');
p('radek 3');
end;
var
i:Integer;
begin
Test;
{ Test2(
procedure (const s:string)
begin
writeln(s);
inc(i);
writeln(i);
end);}
end.
Dobré ne? Deklarujeme anonymní metodu pTest a hnedle do ní přiřadíme kus kódu. Pak ho zavoláme. Jenže místo zavolání ho můžeme předat někam dál,
stačí jen deklarovat metodu s parametrem TAnonProc a hned nám naše malá procedurka může cestovat programem - viz zatím zapoznámkovaná Test2.
Lokální proměnné
Nyní přichází čas na Test2, pokud odkomentujete její volání dostanete následující výstup:
To je teprve zacatek
A znovu
Popojedem
radek 1
1
radek 2
2
radek 3
3
Všimněte si prosím, že proměnná i je deklarovaná v hlavním programu, ale v Test2 je třikrát zavolaná a tudíž je pokaždé zvětšena o 1.
V podstatě je to rozšířením starých známých vložených procedur:
procedure test;
var
i: Integer;
procedure MyInc; // specializovaná procedura zvetšuje přehlednost
begin
inc(i);
end;
begin
i := 10;
MyInc;
MyInc;
end;
Problém závorek
Zde pokud vím vzniká poprvé problém závorek při volání metody bez parametrů.
type
TAnyProc = reference to procedure;
var
AnyProc: TAnyProc;
//musíte zavolat jako AnyProc();
//jinak si kompilátor bude myslet, že chcete referenci a ne volání
Podobně pokud máte funkci, která vrací anonymní metodu (opět zcestný příklad):
function GetMethod: TAnonProc;
begin
Result :=
procedure (const s:string)
begin
ShowMessage (s);
end;
end;
Jak tedy zavoláme vrácenou funkci? Máme tyto možnosti:
var
p: TAnonProc;
begin
p := GetMethod();
p('Hello world');
GetMethod()('Hello world');
GetMethod.Invoke('Hello world'); // Invoke přidáno kompilátorem v případě bez závorek
Předpřipravené anonymní metody
V unitu SysUtils je několik hotových řešení:
type
TProc = reference to procedure;
TProc<T> = reference to procedure (Arg1: T);
TProc<T1,T2> = reference to procedure (Arg1: T1; Arg2: T2);
TProc<T1,T2,T3> = reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3);
TProc<T1,T2,T3,T4> = reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3; Arg4: T4);
procedure UseCode (proc: TProc);
function DoThis (proc: TProc): string;
function DoThat (procInt: TProc<Integer>): string;
type //generické typy
TFunc<TResult> = reference to function: TResult;
TFunc<T,TResult> = reference to function ( Arg1: T): TResult;
TFunc<T1,T2,TResult> = reference to function (Arg1: T1; Arg2: T2): TResult;
TFunc<T1,T2,T3,TResult> = reference to function (Arg1: T1; Arg2: T2; Arg3: T3): TResult;
TPredicate<T> = reference to function (Arg1: T): Boolean;
Třída TThread byla rozšířena o
TThreadProcedure = reference to procedure;
procedure Synchronize(AThreadProc: TThreadProcedure); overload;
Anonymní metody mají hodně použití a mohou zpřehlednit kód (ale i naopak). Mimochodem jedním z hlavních cílů bylo podpora pro budoucí Parallel library, tj. lepší využití víceprocesorových systémů v budoucích verzích.