Saturday, October 16, 2021 21:07

Cuprins >> Obiecte > Proprietăți

Proprietăți

În lecția de astăzi voi vorbi despre unele din noile concepte ale lecției anterioare. Primul subiect din listă: câmpuri și proprietăți. În conformitate cu dragul nostru MSDN, o proprietate este un membru care oferă un mecanism flexibil pentru citirea, scrierea sau calcularea valorii unui câmp privat. Proprietățile pot fi folosite ca și cum ar fi membri de date publice, dar sunt de fapt metode speciale numite accesori (accessors). Singurul lucru pe care definiția de mai sus îl face clar în momentul de față este că proprietățile sunt în strânsă legătură cu câmpurile. Deci, pentru a începe, să luăm exemplul clasei Cuptor din lecția anterioară:

În partea de sus a exemplului meu, avem această porțiune, pe care o vom analiza acum:

În articolul de ieri, am spus că o clasă poate avea câmpuri. Liniile de mai sus sunt câmpurile clasei noastre Cuptor, ele sunt variabile declarate direct în corpul clasei noastre, înafara oricărei metode, funcții etc. Există multe utilizări pentru câmpuri, dar în acest exemplu, ele sunt folosite pentru a stoca valorile proprietăților private, ascunse. De exemplu, proprietatea Greutate își menține valoarea privată folosind variabila de câmp greutate. Nu am învățat încă despre modificatorii de acces, dar probabil ați observat cuvântul cheie private în fața declarației fiecărui câmp. Pentru moment, trebuie să știți doar că acest lucru face ca respectivul câmp să fie accesibil numai din interiorul corpului clasei care declară acest câmp, și invizibil întregii lumi exterioare. Prin convenție, cu excepția cazurilor specifice, câmpurile ar trebui să fie întotdeauna private, iar valoarea lor ar trebui să fie accesibilă lumii exterioare doar prin intermediul proprietăților. Pentru a înțelege mai bine acest lucru, puteți examina următorul exemplul a două proprietăți:

În primul rând, dacă citiți lecția Formatarea Codurilor, ar trebui să vedeți deja că cele două proprietăți sunt sinonime, chiar dacă acestea sunt scrise diferit. Motivul pentru care le-am scris în două moduri diferite este acela că dacă scriem proprietățile noastre în modul în care proprietatea Model este scrisă, vom ocupa o mulțime de linii, iar programul nostru va avea o lungime foarte mare. În comparație cu cel de-al doilea exemplu al unei proprietăți, care ocupă 5 linii de cod, primul ocupă 11. Totuși, așa cum vom vedea în exemplele viitoare, proprietățile pot avea linii de coduri suplimentare, caz în care este de preferat să folosim primul mod de a le scrie. Acesta este motivul pentru care ar trebui să fiți conștienți de ambele moduri de scriere a unei proprietăți: ori de câte ori proprietatea voastră doar primește sau stabilește valoarea unei variabile de câmp, puteți să o scrieți în modul „scurtat”, pentru a economisi spațiu.

Acum, să vedem de fapt ce înseamnă aceste linii. În primul rând, puteți observa că fiecare proprietate are un corp cuprins între {}, la fel ca și clasele, metodele sau funcțiile. Acest lucru l-am văzut și l-am învățat deja. În interiorul acestui corp, avem două construcții: get și set.

Putem efectua doar două acțiuni asupra unei proprietăți: fie să citiți valoarea acesteia, fie să îi atribuiți o valoare. Get și set (numiți și accesori, in engleză accessors) fac exact acest lucru: get (numit și „getter”) ne va da întotdeauna valoarea stocată a proprietății, în timp ce set (numit și „setter”) va stabili întotdeauna o valoare pentru acea proprietate. Deci, când scriem ceva de genul

compilatorul va atribui variabilei greutateCuptorulMeu valoarea proprietății Greutate a clasei Cuptor, folosind intern partea get din corpul proprietății Greutate. De asemenea,

este un exemplu a modului în care atribuim o valoare proprietății Greutate a clasei Cuptor, caz în care, în mod intern, compilatorul va executa partea set din corpul proprietății.

Acum, că știm ce sunt get și set și ce fac ele, să le analizăm mai atent:

S-ar putea să fi observat deja că porțiunea get a proprietății, la fel ca si set, are propriul corp, delimitat de acolade (de fapt, veți observa că destul de multe concepte de programare din C# utilizează acest concept de corp delimitat de acolade).

În interiorul corpului bucății get al proprietății noastre, avem

Cuvântul cheie return nu este nou, am învățat deja despre el. Opreste imediat executia din interiorul acelui corp si returneaza o valoare care ii este oferită. Cuvântul cheie this, pe de altă parte, este complet nou pentru noi. Ca și în cazul multor lecții de pe acest site, nu pot încă explica pe deplin ce înseamnă, deoarece se bazează pe concepte pe care nu le-am explicat încă. Dar, foarte pe scurt, ar trebui să știți că atunci când creăm o clasă – cum ar fi clasa noatră Cuptor, de fapt, creăm o schiță. Ori de câte ori vom începe să folosim obiectele cuptor, nu vom folosi și nu vom modifica schița în sine, ci vom crea copii ale schiței, numit instanțe. Acest lucru este similar și pentru lumea reală: o fabrică de cuptoare nu va vinde niciodată prototipul unui cuptor în sine (schița), ci va crea de fapt copii ale acestuia, care pot fi vândute. Acesta este motivul pentru care folosim cuvântul cheie this, pentru a indica faptul că ne referim nu la schița în sine, ci la copia (instanța) schiței pe care o folosim la momentul respectiv. De exemplu, dacă am crea 100 de instanțe ale obiectului cuptor și am avea acest cod:

atunci cuvântul cheie this din interiorul porțiunii set a proprietății Model din clasa noastră Cuptor se va referi la acea copie specifică a obiectului nostru cuptor, a 37-a copie, NU modelul original (schița). Deci, ori de câte ori folosim cuvântul cheie this, compilatorul înțelege că ne referim la copia pe care o manipulăm în prezent, nu la originalul unic.

Pentru a înțelege complet utilitatea câmpurilor și rolul lor în crearea proprietăților, trebuie să știți că o proprietate nu necesită în mod obligatoriu un câmp. Am fi putut declara proprietățile noastre astfel:

sau versiunea prescurtată, ce folosește doar o linie:

Modul de mai sus de a declara o proprietate este ceea ce programatorii numesc o proprietate implementată automat sau o proprietate automată.
Acum, să presupunem că tocmai am creat cele 100 de copii ale obiectului cuptor și că dorim să afișăm greutatea lor pe consolă:

Deși unii dintre voi s-ar aștepta ca linia de cod de mai sus să genereze o eroare de compilator (Use of unassigned variable), deoarece proprietatea nu are o valoare de returnat, aceasta nu este de fapt în întregime adevărat. Intern, C# va genera automat un câmp pentru proprietatea noastră, care este inițializat cu valoarea implicită a respectivului tip. În cazul nostru, valoarea implicită pentru int fiind 0, linia de mai sus a codului va afișa 0 pe consolă, chiar dacă nu am atribuit valoarea 0 nicăieri în proprietatea noastră. Deci, acest lucru ridică întrebarea logică: dacă C# creează câmpuri automate pentru proprietățile noastre, de ce ar trebui să declarăm în mod specific câmpuri? De ce să nu le lăsăm să fie generate automat? Există câteva motive pentru care trebuie să specificăm explicit câmpurile, cele mai multe dintre ele aparținând unor tehnici de programare destul de avansate (cum ar fi imutabilitatea, etc). Dar pentru nivelul nostru de programare începător, să luăm în considerare următorul exemplu:

Dacă vom crea o copie a schiței clasei Angajat, putem face acest lucru:

Ați văzut vreodată un angajat cu vârsta de 200 de ani? Eu nu… Deci, nu ar fi minunat dacă am putea cumva „forța” proprietatea Varsta să accepte doar o anumită gamă de valori? Acesta este momentul când câmpurile ne vin în ajutor:

Acum am oferit o logică suplimentară proprietății noastre, și niciodată nu veți vedea un obiect Angajat cu vârsta de -5 ani. Acest lucru nu ar fi fost posibil dacă am fi folosit proprietăți automate (da, știu că nu am învățat încă să generăm excepții, așa că folosiți exemplul așa cum este, pentru moment).

În concluzie, utilizați proprietățile implementate automat unde trebuie doar să stocați o proprietate care nu necesită logică suplimentară, și proprietăți complet calificate atunci când este necesar.

Vorbind despre setteri, ei au prioritate mai mică decât returnarea unui literal în getter. De exemplu, următorul cod este perfect valid:

dar setterul proprietății este într-un fel inutil, deoarece proprietatea va returna întotdeauna 10, indiferent cât de mult încercăm să stabilim o valoare nouă.

O altă caracteristică importantă a proprietăților este că pot fi read only (doar citire). Într-un fel, putem spune că și exemplul de cod de mai sus este read only, deoarece ar returna oricum aceeași valoare, indiferent dacă încercăm să setăm o altă valoare. Dar, cu toate acestea, nu acela este modul obișnuit de a declara o proprietate numai pentru citire. În schimb, o facem așa:

Ori de câte ori veți încerca să setați o valoare pentru proprietatea de mai sus, veți obține o eroare de compilator: Proprietatea sau indexatorul ‘Cuptor.ProprietateaMea’ nu poate fi atribuit – este doar citit (Property or indexer ‘Cuptor.ProprietateaMea’ cannot be assigned to — it is read only).

Deci, care este utilitatea unei proprietăți căreia nu îi putem atribui nici o valoare implicită? Da, am putea utiliza o variabilă de câmp pentru a seta o valoare implicită, după cum urmează:

Dar asta ar elimina scopul folosirii proprietăților auto-implementate, nu-i așa? În acest caz, avem de fapt următoarea soluție:

În codul de mai sus, avem o proprietate implementată automat care are un setter privat (private setter), ceea ce înseamnă că putem să-i stabilim valoarea numai din clasa care în care a fost declarată proprietatea. Prin urmare, ni se permite să facem acest lucru:

dar nu avem voie să facem asta:

Acest lucru se datorează faptului că un setter privat ne permite să atribuim o valoare proprietății doar din interiorul corpului acelei clase, și nu din lumea exterioară. Nu putem stabili o valoare pentru acea proprietate atunci când creăm o copie a obiectului nostru Cuptor.

Începând cu C# versiunea 5.0, există de asemenea o notație specială pentru proprietățile read only, și anume:

dar rareori am văzut-o folosită. Cu toate acestea, dacă vă place, folosiți-o, nimeni nu vă oprește.

Desigur, avem și proprietăți write only (doar scriere), dar ele sunt folosite foarte rar. Puteți măcar să vă gândiți la un exemplu în care trebuie doar să setați date, dar niciodată să le citiți?

Tags: , ,

Leave a Reply



Follow the white rabbit