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:
1type
2 TLogProc = procedure (const sText:string);
3 TLogMethod = procedure (const sText:string) of object;
4
5 TAnonProc = reference to procedure (const sText:string);
Jednoduchý neužitečný příklad
1program Project1;
2
3
4
5uses
6 SysUtils, TypInfo;
7type
8 TAnonProc = reference to procedure (const sText:string);
9
10procedure Test;
11var
12 pTest: TAnonProc;
13begin
14 pTest :=
15 procedure (const s: string)
16 begin
17 writeln(s);
18 end;
19 pTest ('To je teprve zacatek');
20 pTest ('A znovu');
21 pTest ('Popojedem');
22end;
23
24procedure Test2(p:TAnonProc);
25begin
26 p('radek 1');
27 p('radek 2');
28 p('radek 3');
29end;
30
31var
32 i:Integer;
33begin
34 Test;
35
42
43end.
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:
1procedure test;
2var
3 i: Integer;
4 procedure MyInc;
5 begin
6 inc(i);
7 end;
8begin
9 i := 10;
10 MyInc;
11 MyInc;
12end;
Problém závorek
Zde pokud vím vzniká poprvé problém závorek při volání metody bez parametrů.
1type
2 TAnyProc = reference to procedure;
3var
4 AnyProc: TAnyProc;
5
6
7
Podobně pokud máte funkci, která vrací anonymní metodu (opět zcestný příklad):
1function GetMethod: TAnonProc;
2begin
3 Result :=
4 procedure (const s:string)
5 begin
6 ShowMessage (s);
7 end;
8end;
Jak tedy zavoláme vrácenou funkci? Máme tyto možnosti:
1var
2 p: TAnonProc;
3begin
4 p := GetMethod();
5 p('Hello world');
6
7 GetMethod()('Hello world');
8 GetMethod.Invoke('Hello world');
Předpřipravené anonymní metody
V unitu SysUtils je několik hotových řešení:
1type
2 TProc = reference to procedure;
3 TProc<T> = reference to procedure (Arg1: T);
4 TProc<T1,T2> = reference to procedure (Arg1: T1; Arg2: T2);
5 TProc<T1,T2,T3> = reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3);
6 TProc<T1,T2,T3,T4> = reference to procedure (Arg1: T1; Arg2: T2; Arg3: T3; Arg4: T4);
7
8procedure UseCode (proc: TProc);
9function DoThis (proc: TProc): string;
10function DoThat (procInt: TProc<Integer>): string;
11
12type
13 TFunc<TResult> = reference to function: TResult;
14 TFunc<T,TResult> = reference to function ( Arg1: T): TResult;
15 TFunc<T1,T2,TResult> = reference to function (Arg1: T1; Arg2: T2): TResult;
16 TFunc<T1,T2,T3,TResult> = reference to function (Arg1: T1; Arg2: T2; Arg3: T3): TResult;
17 TPredicate<T> = reference to function (Arg1: T): Boolean;
Třída TThread byla rozšířena o
1TThreadProcedure = reference to procedure;
2
3procedure 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.