Delphi.cz

Český portál Delphi

Anonymní metody - druhé podání

V době vydání Delphi 2009 jsem popisoval Anonymní metody (v jiných jazycích někdy jako closure), ale nějak mi nedošlo to hlavní, resp. nějak jsem nepochopil důležitost jedné vlastnosti a to je "capture", tj. zachycení stavu lokálních proměnných čímž se zásadně odlišují od typu funkce nebo metoda.

Mějme následující kód u formuláře. Máme dvě anonymní, které mají stejné parametry, ale jedna je deklarována jako nový typ, druhá používá generickou deklaraci anonymní metody ze sysutils (to aby bylo patrno, že generika nejsou jen kolekce a listy).

// SysUtils: TProc<T1,T2> = reference to procedure (Arg1: T1; Arg2: T2);

Takže na začátku ve formcreate jsou obě reference vyprázdněny, jedno tlačítko je nastaví, druhé je zavolá.

type
  TRefIntProc = reference to procedure (const sVal:string; iAdd: Integer);

  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    FIntProc: TRefIntProc;
    FIntProc2: TProc<string, Integer>;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
  i: integer;
begin
  i := 3;
  // nastav první anonymní metodu
  FIntProc := procedure (const sVal:string; iAdd: Integer)
  begin
    inc(i, iAdd);
    ShowMessage(sVal+' IntProc:'+IntToStr(i));
  end;
  // a druhou
  FIntProc2 := procedure (sVal:string; iAdd: Integer)
  begin
    inc(i, iAdd);
    ShowMessage(sVal+' IntProc2:'+IntToStr(i));
  end;
  inc(i); //4
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  if assigned(FIntProc) then
    FIntProc('Button2Click', 4);
  if assigned(FIntProc2) then
    FIntProc2('Button2Click', 3);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  FIntProc := nil;
  FIntProc2 := nil;
end;

Všimněte si, prosím, v obsluze Button1Click práce s proměnnou i, pro jistotu i = 4 na konci obsluhy. Anonymní metody jsou přiřazeny v okamžiku kdy i = 3.

V obsluze Button2Click je provedeno volání obou uložených metod (přes assigned, kdyby to někoho napadlo volat před Button1). Kontrolní otázka: Co se stane po prvním a po druhém kliknutí?

Pro řešení je nutno chápat, že přiřazení anonymní metody způsobí, že lokální proměnné jsou zachyceny a žijí tak dlouho jako anonymní metoda. Je to řešeno nějak přes interface a počítání referencí a to tak, že jsou lokální proměnné zkopírovány včetně parametrů a dál si žijí a anonymní metoda s nimi dál pracuje.

Tj. po kliknutí se vypíše

Button2Click IntProc:8
[OK]

Button2Click IntProc2:11
[OK]

Po dalším kliknutí se hodnoty změní na 15 a 18.

Nyní je patrné, že je pravda co jsem napsal a lokální proměnná i je někde uložena a anonymní metoda s ní může pracovat. Podle mne je toto zachování stavu pro pozdější použití důležitá a zajímavá vlastnost.

Minule jsem psal, že hodně síly chápu v synchronizacích např. GUI. Doporučoval jsem je používat pro synchronize, ale nějak jsem opomněl

TThread.Queue(AThreadProc: TThreadProcedure)

což je asynchronní synchronizace UI v hlavním vlákně.

Nehledě na jejich použití vzhledem k paralelnímu programování.

Datum: 2015-01-16 00:01:00 Tagy: anonymni metody