Anonymní metody - druhé podání

vložil Radek Červinka 16. ledna 2015 01:01

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

Tagy:

Komentáře

21.1.2015 23:35:35 #

Ondřej Pokorný

Jen malý poznatek: Používat proměnné v anonymních metodách, které by v samotném spuštění anonymní metody vlastně neměly existovat nebo se s nimi vůbec nemělo operovat, není úplně bezpečné a člověk musí vědět, co dělá.

Proč? Vem v úvahu např. tento kód:

procedure TestAnonymous;
type
  TProc = reference to procedure;
var
  I: Integer;
  P: TProc;
begin
  P := procedure
    begin
      Dec(I);
    end;

  for I := 0 to 1 do
  begin
    P();
    ShowMessage(IntToStr(I));
  end;
end;

Tady je sice situace jasná, ale může se to dost zamotat ;) Neříkám, že by se to nemělo využívat, musí se jen dávat pozor...

Mimochodem, proto taky Result a ostatní var/out parametry nejde podobně zneužít: http://stackoverflow.com/questions/801147/scope-of-anonymous-methods

Ondřej Pokorný

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ů