MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Închideri (closure)

O închidere (closure) reprezintă un obiect funcție care memorează mediul său lexical chiar și atunci când este executată în afara domeniului său de vizibilitate.

Tehnic, o închidere include trei componente:

  • Funcția externă, care definește un anumit domeniu de vizibilitate și în care sunt definite unele variabile și parametri - mediul lexical
  • Variabilele și parametrii (mediul lexical), care sunt definite în funcția externă
  • Funcția internă, care utilizează variabilele și parametrii funcției externe

În limbajul C#, închiderile pot fi realizate în diferite moduri - cu ajutorul funcțiilor locale și expresiilor lambda.

Să analizăm crearea închiderilor prin funcții locale:

var fn = Outer();   // fn = Inner, deoarece metoda Outer returnează funcția Inner
// apelăm funcția internă Inner
fn();   // 6
fn();   // 7
fn();   // 8

Action Outer()  // metodă sau funcție externă
{
   int x = 5;  // mediu lexical - variabilă locală
   void Inner()    // funcție locală
   {
       x++;        // operații cu mediul lexical
       Console.WriteLine(x);
   }
   return Inner;   // returnăm funcția locală
}

Aici, metoda Outer are ca tip de returnare tipul Action, adică metoda returnează o funcție care nu acceptă parametri și are tipul void.

Action Outer()

În interiorul metodei Outer este definită variabila x - aceasta este mediul lexical pentru funcția internă:

int x = 5;

De asemenea, în interiorul metodei Outer este definită funcția internă - funcția locală Inner, care accesează mediul său lexical - variabila x - îi mărește valoarea cu unu și o afișează în consolă:

void Inner()
{
   x++;
   Console.WriteLine(x);
}

Această funcție locală este returnată de metoda Outer:

return Inner;

În program, apelăm metoda Outer și obținem în variabila fn funcția locală Inner:

var fn = Outer();

Variabila fn reprezintă o închidere, adică combină două lucruri: funcția și mediul în care funcția a fost creată. Și, deși am obținut funcția locală și o putem apela în afara metodei în care a fost definită, aceasta își amintește mediul său lexical și poate să-l acceseze și să-l modifice, ceea ce vedem prin ieșirea în consolă:

fn();   // 6
fn();   // 7
fn();   // 8

Realizarea cu ajutorul expresiilor lambda

Cu ajutorul lambdelor putem simplifica definirea unei închideri:

var outerFn = () =>
{
   int x = 10;
   var innerFn = () => Console.WriteLine(++x);
   return innerFn;
};

var fn = outerFn();   // fn = innerFn, deoarece outerFn returnează innerFn
// apelăm innerFn
fn();   // 11
fn();   // 12
fn();   // 13

Utilizarea parametrilor

Pe lângă variabilele externe, mediul lexical include și parametrii metodei înconjurătoare. Să analizăm utilizarea parametrilor:

var fn = Multiply(5);

Console.WriteLine(fn(5));   // 25
Console.WriteLine(fn(6));   // 30
Console.WriteLine(fn(7));   // 35

Operation Multiply(int n)
{
   int Inner(int m)
   {
       return n * m;
   }
   return Inner;
}
delegate int Operation(int n);

Aici, funcția externă - metoda Multiply returnează o funcție care acceptă un număr de tip int și returnează un număr de tip int. Pentru aceasta este definit delegatul Operation, care va reprezenta tipul returnat:

delegate int Operation(int n);

Deși am putea folosi și delegatul încorporat Func<int, int>.

Apelul metodei Multiply() returnează funcția locală care corespunde semnăturii delegatului Operation:

int Inner(int m)
{
   return n * m;
}

Această funcție își amintește mediul în care a fost creată, în special valoarea parametrului n. În plus, acceptă un parametru și returnează produsul parametrilor n și m.

În final, la apelul metodei Multiply, se definește variabila fn, care primește funcția locală Inner și mediul său lexical - valoarea parametrului n:

var fn = Multiply(5);

În acest caz, parametrul n este egal cu 5.

La apelul funcției locale, de exemplu, în cazul:

Console.WriteLine(fn(6));   // 30

Numărul 6 este transmis pentru parametrul m al funcției locale, care returnează produsul n și m, adică 5 * 6 = 30.

De asemenea, putem simplifica tot acest cod cu ajutorul lambdelor:

var multiply = (int n) => (int m) => n * m;

var fn = multiply(5);

Console.WriteLine(fn(5));   // 25
Console.WriteLine(fn(6));   // 30
Console.WriteLine(fn(7));   // 35
← Lecția anterioară Lecția următoare →