Wednesday, April 24, 2024 09:30

Cuprins >> LINQ > Instrucțiunea yield break

Instrucțiunea yield break

Ori de câte ori folosim cuvântul cheie yield într-o declarație, indicăm că metoda, operatorul sau accesorul get în care apare este un iterator. Desigur, din moment ce știm că iteratorii sunt folosiți să… duh! itereze pe colecții de date și, din moment ce știm că atunci când iterăm pe o colecție, putem folosi cuvântul cheie break pentru a termina imediat iterația, este evident că de fiecare dată când folosim o instrucțiune yield return pentru a returna valori dintr-un iterator, cum am arătat în lecția anterioară, putem folosi de asemenea și yield break pentru a termina iterația iteratorului menționat.

Aceasta este versiunea scurtă a acestei lecții: yield break este oarecum similar cu un break normal.

Acum, să extindem acest concept cu o explicație mai detaliată. În lecția anterioară, am avut inițial acest cod:


O metodă simplă în interiorul căreia generam o grămadă de numere aleatorii, bazate pe parametrul _numarElemente, le stocăm într-o listă și, în cele din urmă, returnăm lista respectivă și afișăm elementele sale la consolă.

Ulterior, explicam cum putem evita cu totul crearea unei liste temporare și necesitatea de a aștepta generarea tuturor numerelor aleatorii înainte de a returna întreaga listă simultan, folosind o declarație yield return:

Vizualizează schimbări cod


Legend:

  • liniile verzi cu un semn plus lângă numerele liniilor sunt linii noi adăugate
  • liniile rosii cu un semn minus lângă numerele liniilor sunt linii vechi șterse




Acum, continuând cu acest exemplu, să presupunem că vrem să eliminăm parametrul _numarElemente, astfel încât funcția GenereazaNumereAleatorii() să genereze numere aleatorii la nesfârșit, până când încetăm să le cerem, astfel:

Vizualizează schimbări cod


Legend:

  • liniile verzi cu un semn plus lângă numerele liniilor sunt linii noi adăugate
  • liniile rosii cu un semn minus lângă numerele liniilor sunt linii vechi șterse


În funcția mea GenereazaNumereAleatorii(), am înlocuit bucla for cu o buclă while infinită, iar în interiorul metodei Main(), am cerut aceste numere aleatorii continuu până când numărul generat este divizibil cu 100, caz în care am utilizat operatorul break pentru a opri apelarea funcției GenereazaNumereAleatorii(). Acesta este rezultatul:

Aici lucrurile încep să devină interesante. Ce s-ar întâmpla dacă nu aș avea declarația yield return și aș fi forțată să folosesc o listă, la fel ca în exemplul de la începutul acestei lecții? Ar fi cam dificil:

Vizualizează schimbări cod


Legend:

  • liniile verzi cu un semn plus lângă numerele liniilor sunt linii noi adăugate
  • liniile rosii cu un semn minus lângă numerele liniilor sunt linii vechi șterse


Codul se compilează fără probleme, dar când execut programul, se va bloca:

Probabil puteți identifica deja problema, chiar și compilatorul este suficient de inteligent pentru a sublinia instrucțiunea return _lista_temporara; cu o linie zig-zag, pentru că știe că execuția nu va atinge niciodată acel punct, deoarece va fi blocat pentru totdeauna într-o buclă infinită while. Cu alte cuvinte, va continua să adauge elemente la variabila _lista_temporara până când va rămâne în cele din urmă fără memorie RAM și programul se va bloca.

În acest caz, putem încerca să folosim operatorul break în bucla infinită while, iar apoi să verificăm dacă numărul este divizibil cu 100 în interiorul ei, astfel:

Vizualizează schimbări cod


Legend:

  • liniile verzi cu un semn plus lângă numerele liniilor sunt linii noi adăugate
  • liniile rosii cu un semn minus lângă numerele liniilor sunt linii vechi șterse


Caz în care programul va rula fără probleme:

Acum putem reveni la versiunea de cod în care nu foloseam deloc o listă, înlocuind-o cu o instrucțiune yield return, și o instrucțiune yield break în loc de un break simplu:

Vizualizează schimbări cod


Legend:

  • liniile verzi cu un semn plus lângă numerele liniilor sunt linii noi adăugate
  • liniile rosii cu un semn minus lângă numerele liniilor sunt linii vechi șterse

Un lucru de reținut este faptul că, dacă utilizați yield break, nu mai puteți utiliza operatorul return, sunteți forțați să utilizați yield return deoarece transformați efectiv metoda respectivă într-un iterator. Compilatorul vă va da această eroare: Eroare CS1622: Nu pot returna o valoare de la un iterator. Utilizați declarația yield return pentru a returna o valoare, sau yield break pentru a termina iterația (Error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration). Deci, o metodă nu poate folosi atât return, cât și yield în același timp, trebuie să alegeți doar una dintre ele, deoarece una este utilizată de bucle normale, cealaltă este utilizată de blocuri iteratoare, care, așa cum știm deja, sunt implementate diferit față de buclele normale.

În cele din urmă, yield break poate fi văzut ca având o singură funcționalitate, chiar dacă ați putea crede că are două: deoarece îl puteți folosi într-o buclă, la fel ca operatorul break obișnuit, ați putea crede că ar avea același comportament, și v-ați înșela. Yield break se comportă de fapt ca o instrucțiune return, ce, spre deosebire de break, care doar încheie iterația buclei și continuă cu instruțiunile care urmează după buclă, va încheia de fapt execuția întregii metode și o va întoarce la locul în care metoda a fost apelată. Deci, yield break nu va termina doar bucla, va ieși din funcție în totalitate, la fel ca return, și nu va avea niciodată o a doua funcționalitate care emulează break.

Tags: , , , , , , ,

Leave a Reply



Follow the white rabbit