În câteva dintre lecțiile anterioare, am folosit functii – metode care nu numai că pot fi apelate, pot efectua o acțiune, dar care pot și returna o valoare apelantului – bucata de cod pe care le-a invocat. Vă puteți imagina acest lucru și ca plasarea valoarii returnate în locul în care funcția a fost invocată. Putem:
- atribui unei variabile – valoarea returnată poate fi direct atribuită unei variabile care are același tip ca și tipul de returnare al funcției:
12//PreiaLogoCompanie() returneaza un stringstring logoCompanie = PreiaLogoCompanie(); - utiliza în expresii – când o funcție returnează o valoare, această valoare poate fi utilizată direct în alte expresii și calcule:
1float pretTotal = PreiaPretPerBucata() * cantitate; - utiliza ca parametrii de metodă – este perfect valid să utilizăm valoarea returnată a unei funcții ca parametru pentru o altă metodă sau apel de funcție:
1Console.WriteLine(PreiaLogoCompanie());
Așa cum am explicat în câteva din lecțiile anterioare, ceea ce diferențiază o metodă de o funcție este faptul că acesta din urmă returnează întotdeauna o valoare, și trebuie să utilizeze întotdeauna o declarație return. Tiparul de bază pentru o funcție este acesta:
1 2 3 4 5 |
static <tip_returnat> <nume_functie>(<lista_parametrii>) { //cod pentru prelucrarea datelor return <rezultat_functie>; } |
Întotdeauna amintiți-vă că valoarea returnată trebuie să fie de același tip cu tipul de returnare al funcției, așa cum l-ați declarat. Nu puteți avea acest lucru:
1 2 3 4 5 |
static float FunctiaMea() { double variabilaDouble = 0.0; return variabilaDouble; } |
Deci, dacă am declarat funcția noastră ca returnând o valoare de tip float, ea trebuie să returneze o valoare float. În caz contrar, compilatorul ne va da o eroare: Nu pot converti în mod implicit tipul „dublu“ în „float“ (Cannot implicitly convert type ‘double’ to ‘float’).
Cu toate acestea, există un număr mic de cazuri în care este permisă returnarea unui alt tip de valoare decât tipul de returnare declarat. Observați:
1 2 3 4 5 |
static float FunctiaMea() { int numarulMeuIntreg = 3; return numarulMeuIntreg; } |
Spre surprinderea voastră, chiar dacă funcția noastră declară în semnătura sa că returnează un float, ea returnează un număr întreg, iar compilatorul nu se plânge cu privire la acest lucru. Asta deoarece, chiar dacă tipul returnat nu este de tip float, acesta poate fi convertit în mod implicit într-un float.
Cu toate acestea, ar fi de preferat să rămâneți la regula de a returna același tip ca și tipul declarat în semnătura funcției, pentru a evita confuziile.
Un alt lucru de care ar trebui să fiți conștienți este ceea ce se întâmplă cu codul care urmează după cuvântul cheie return. Considerați:
1 2 3 4 5 6 |
static string FunctiaMea() { //cod aici return "Buna Lume!"; //alt cod aici } |
Unii dintre voi s-ar putea aștepta ca „alt cod aici“ să fie executat, așa cum am învățat că execuția continuă de sus în jos, într-un mod liniar. Acest lucru este de fapt greșit, deoarece cuvântul cheie return va încheia imediat executarea funcției, și va returna valoarea pe care i-o oferim, în exemplul nostru, „Buna Lume!“. Tot codul care urmează după cuvântul cheie return nu va fi executat. De fapt, compilatorul chiar ne avertizează cu privire la acest lucru: Cod inaccesibil detectat (Unreachable code detected). În cuvinte simple, aceasta înseamnă că acea bucată de cod nu va fi executată, deoarece cuvântul cheie return va termina executia functiei si va returna rezultatul acesteia.
Ne este permis și să returnăm în mod direct o expresie:
1 2 3 4 |
static int Inmultire(int x, int y) { return x * y; } |
În acest caz, compilatorul va evalua mai întâi rezultatul expresiei x * y, și numai apoi va returna valoarea rezultată. Este de preferat să se evite acest lucru atunci când instrucțiunea de returnare devine complicată sau lungă.
Pot metodele să aibă și ele o instrucțiune return?
Răspunsul este da, dar nu în forma care v-ați imagina-o. Da, știu că am spus de multe ori că metodele nu returnează o valoare, și că funcțiile o fac, dar înainte de a arăta cu degetul spre mine, uitați-vă la următorul exemplu:
1 2 3 4 5 6 7 8 |
static void StergeFisier(string caleFisier) { if (!File.Exists(caleFisier)) return; else File.Delete(caleFisier); Console.WriteLine("Fisier sters!"); } |
Deci, avem o metodă pentru ștergerea unui fișier, cu un parametru de tip string unde specificați locația actuală a fișierului pe care vreți să-l ștergeți. Ce s-ar întâmpla dacă am încerca să ștergem un fișier care nu mai există? Programul va genera cel mai probabil o eroare. Acesta este motivul pentru care vom verifica mai întâi dacă fișierul există, și dacă nu (vă mai amintiți de operatorul boolean de „negație“ „!“? In acest caz, se traduce ca „dacă fișierul NU există“), utilizăm cuvântul cheie return. La fel ca pentru funcții, operatorul return va opri imediat executarea metodei noastre și va returna fluxul de executie unde a fost înainte de apelul metodei. Singura diferență între o metodă și o funcție este că o metodă nu poate returna o valoare. Nu putem returna un int sau un float, sau orice altceva. De aceea, putem folosi doar cuvântul cheie return, urmat de nici o altă instrucțiune. În acest caz, îl folosim doar pentru a opri imediat executarea metodei și a reveni acolo unde metoda a fost apelată.
Ce s-ar întâmpla dacă funcția noastră ar fi foarte complexă, cu o mulțime de ramificări ale codului? Cum folosim cuvântul cheie return în mai multe locuri?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static int ComparaNumere(int x, int y) { if (x > y) { return 1; } else if (x == y) { return 0; } else { return -1; } } |
Codul de mai sus are doi parametri de tip numere întregi și efectuează anumite verificări cu ei. În cazul în care primul este mai mare decât al doilea, se returnează o valoare. Dacă sunt egali, se returnează o altă valoare, și așa mai departe. Acest lucru este perfect valabil, si ni se permite să avem mai multe declarații return, deoarece acestea sunt separate de funcționalitatea instrucțiunii if else. După cum știm deja, nu putem avea două părți ale unei instrucțiuni if else care să se execute simultan; e fie una, fie alta. Acest lucru este ceea ce ne permite să returnăm mai multe valori în mai multe locuri, iar acest lucru este de asemenea, ceea ce ne duce într-o zonă periculoasă. Luați în considerare următorul exemplu:
1 2 3 4 5 6 7 8 9 10 11 |
static int ComparaNumere(int x, int y) { if (x > y) { return 1; } else if (x == y) { return 0; } } |
Spre surprinderea voastră, codul de mai sus va genera o eroare de compilare: „Program.ComparaNumere(int, int)“: nu toate căile de cod returnează o valoare (‘Program.ComparaNumere(int, int)’: not all code paths return a value). De ce acest lucru? Ei bine, să ne gândim puțin: ca și în exemplul anterior, avem o funcție cu doi parametri întregi, asupra cărora efectuăm unele verificări. În cazul în care primul este mai mare decât al doilea, returnăm 1. În cazul în care sunt egali, returnăm 0. Și……. dacă primul este mai mic decât al doilea? Aceasta este ceea ce generează eroarea, compilatorul știe că există acest caz suplimentar pe care noi nu îl verificăm, și pentru că o funcție trebuie să returneze o valoare, aceasta va genera o eroare, din moment ce nu se returnează o valoare pentru cazul în care x este mai mic decât y. În concluzie, atunci când vedeți eroarea de mai sus, verificați întotdeauna toate cazurile care se pot întâmpla, și pe care nu le implementați.
Conceptele explicate în această lecție sunt prezentate și vizual, ca parte a următorului videoclip:
Tags: alți operatori, cuvinte cheie C#, funcții, operatori, operatorul return, return