- Tutoriale C# – Prezentare
- Tutoriale C# – Prezentarea limbajului
- Tutoriale C# – Elemente comune cu limbajele C și C++
- Tutoriale C# – Programarea orientată pe obiecte – 1 –
- Tutoriale C# – Programarea orientată pe obiecte – 2 –
- Tutoriale C# – Programarea orientată pe obiecte – 3 –
- Tutoriale C# – Delegări, evenimente, generice
Acum că am văzut cum este structurată platforma .NET precum și mecanismul de compilare a codului C# în cadrul acestei platforme, a venit momentul să ne orientăm strict pe limbaj.
1. Elemente generale
Am precizat în tutorialul precedent că limbajul C# este un limbaj multiparadigmă, ceea ce s-ar traduce prin ”un limbaj care acceptă mai multe paradigme (sau stiluri) de programare”, cum ar fi:
- programarea procedurală – specifică limbajului C, se definește prin acel stil de programare care se bazează pe modularea aplicațiilor, adică structurarea codului în mai multe subprograme (sau funcții), fiecare cu un anumit task (sarcină). Acest lucru facilitează foarte mult lizibilitatea și claritatea codului, în sensul că întreaga aplicație are o structură bine definită, ceea ce ușurează foarte mult depanarea (debugging) programului – în momentul în care apare un bug, acesta se află în mod evident într-unul dintre subprogramele definite.
- programarea generică – acceptată de limbajul C++, se referă la capacitatea unui anumit limbaj de a pune la dispoziția dezvoltatorului facilitatea de a crea funcții și clase, independente de tipul datelor. Spre exemplu, dacă avem o funcție care calculează minimul intre două numere, acele numere pot fi întregi sau reale. În programarea procedurală, trebuia definită o funcție pentru fiecare tip de parametrii. În programarea generică, se definește o singură funcție, care acceptă un parametru de un tip general (notat cu T, în majoritatea cazurilor), și care calculează ceea ce trebuie, în funcție de acest tip general.
- programarea orientată pe obiect – definitorie pentru limbajul C++, practic această facilitate a constituit fundamentul limbajului, în sensul că Bjarne Stroustrup (creatorul limbajului C++), a inteționat să creeze un limbaj superior C-ului, de aceea l-a numit în primii ani de la lansare ”C with classes” – C cu clase. Acest stil de programare a influențat major întreaga industrie a software-ului. C# acceptă, în mod evident, programarea orientată pe obiect, cu toate calitățile care îi revin.
- programarea orientată pe eveniment – se referă, în principiu, la capacitatea unui limbaj de a crea aplicații (în majoritatea cazurilor, cu interfață grafică), sensibile la evenimente din partea utilizatorului, spre exemplu: click pe un buton, apăsarea unei taste, mișcarea mouse-ului pe deasupra ferestrei, etc. Este un stil de programare care are ca rădăcină programarea orientată pe obiect, dar se individualizează prin capacitatea acesteia de a interacționa cu utilizatorul. Practic, orice aplicație care realizează un anumit lucru, util, de regulă, cerut de un anumit utilizator (client), trebuie să interacționeze cu el într-un anumit fel. POE este acest mijloc de comunicare, indispensabil oricărui dezvoltator.
2. Tipuri de date
Știm că un algoritm este de fapt o metodă de rezolvare a unei anumite probleme. Transpunerea unui algoritm într-un limbaj de programare se numește implementare. Pentru a putea realiza implementarea unui algoritm într-un astfel de limbaj, sunt necesare niște date. Algoritmul preia date de intrare, le procesează și generează date de ieșire. Aceste date trebuie să fie stocate în memorie, pentru a putea fi utilizate/procesate. Pentru a putea stoca în memorie datele, limbajele de programare au capacitatea de a crea niște elemente capabile să stocheze date în memorie. Aceste elemente se numesc tipuri de date.
Prin tip de date înțelegem o entitate care permite declararea de variabile cu un anumit specific. O variabilă este o structură definită de două elemente deosebit de importante: numele, care specifică zona de memorie alocată pentru stocarea datelor; setul de operații specifice tipului de date.
Presupun că e ceață densă acum, deci să mai încălzim un pic atmosfera: tipurile de date pot fi: întregi, reale, reale_cu_precizie_mare, booleane (acceptă două valori – true sau false), caracter, etc. Acestea sunt tipuri predefinite în limbaj (built-in). De asemenea, utilizatorul își poate defini propriile tipuri (user-defined), cu ajutorul claselor, despre care vom vorbi într-un tutorial viitor.
Aceste tipuri de date se împart în alte două categorii (toate la un loc), și anume:
- tipuri valoare – corespund variabilelor locale care se memorează în segmentul de stivă. De regulă, având în vedere că ele sunt memorate pe stivă, se pot accesa mai ușor, deci atunci când vine vorba de un nivel mare de cod, se poate remarca o creștere semnificativă a performanței programului. Aceste tipuri valoare corespund tipurilor predefinite, structurilor, enumerărilor.
- tipuri referință – corespund tipurilor user-defined, deci obiectelor create cu ajutorul claselor. Aceste variabile se memorează într-o zonă specială de memorie, denumită memoria Heap.
De asemenea, limbajul C# admite și lucrul cu pointeri (pentru cei familiarizați cu limbajul C), dar este o practică nepromovată de creatorii .NET pentru faptul că orice cod care utilizează pointeri este considerat unmanaged-code, deci cod nespecific platformei. Din rațiuni de performanță, se poate scrie un asemenea cod, încadrat în keyword-ul unsafe, ceea ce specifică CLR-ului să nu analizeze codul respectiv, ci doar să îl execute. Acest lucru trebuie făcut în totală cunoștință de cauză, întrucât poate rezulta un damage destul de serios dacă nu s-au acoperit toate cazurile aruncătoare de excepții.
În acest tabel avem toate tipurile predefinite din limbajul C#. Pe prima coloană se află numele tipului, pe a doua coloană se află dimensiunea, în biți, a tipului respectiv iar pe coloana din dreapta avem un range de valori pe care fiecare tip îl poate lua. Nu trebuie memorat acest tabel, sub nicio formă, dar este foarte util să știi cât mai multe lucruri despre variabilele cu care lucrezi. Limbajele C și C++ au, majoritar, aceleași tipuri de date ca și limbajul C#.
Cum se face declararea unei variabile:
int variabila;
Cum se face atribuirea unei valori:
variabila = 200
E exact la fel ca în C și C++. Nicio schimbare majoră.
De reținut următoarele elemente orientative: tipul decimal, reține numere de precizie mare, fără pierdere de zecimale; tipul long reține numere mari, în intervalul specificat, numere întregi, deci pot fi și negative; tipul ulong reține numere pozitive mari. Este exact ca tipul long, cu deosebirea că acel u prezent în fața numelui, provine de la unsigned, care se traduce prin ”fără semn”, deci nu are semn (negativ, evident). Ca urmare, îi crește range-ul de valori. Aceeași regulă se aplică și la celelalte tipuri care au u în față.
Tipul bool este un tip special, denumit tip boolean. Variabilele de acest tip pot memora două valori: true sau false; 1 sau 0. Sunt exact aceleași chestiuni. Acest tip se folosește des la flag-uri, atunci când vrei să vezi dacă controlul programului a intrat printr-o anumită structură de decizie sau repetitivă.
Tipul int reține numere întregi în intervalul specificat.
Acum o să vorbim despre tipuri definite de utilizator:
- Clasele – sunt elemente deosebit de importante în cadrul limbajului, precum și deosebit de importante în cadrul celorlalte limbaje care acceptă paradigma OOP. Acestea se declară cu keyword-ul class și pot conține metode (funcții care îndeplinesc un anumit task), câmpuri (variabile de clasă), sau alte clase (mai rar). Spre exemplu:
class Masini
{ public int an;
public string marca;
private int calculeazaTaxaPoluare(int an, int emisiiCo2);
} - Structurile – sunt tipuri valoare (deci se alocă pe stivă), asemănătoare claselor, dar se diferențiază prin faptul că nu acceptă unele elemente specifice claselor, precum: moștenire, încapsularea datelor, etc. (vom vorbi în secțiuni viitoare despre acestea)
- Enumerări – se declară cu cuvântul cheie enum, au rolul de a structura o colecție de elemente similare din punct de vedere al relevanței, ușurând accesibilitatea programatorului la organizarea acestora.
- Delegări și interfețe – sunt tipuri speciale, specifice limbajului, care au aplicabilitate la nivel înalt, și care imprimă o capacitate de înțelegere destul de ridicată. Practic, delegările sunt tipuri care memorează prototipuri de funcții care au aceeași semnătură (același tip de returnare, același tip al parametrilor). Locul în care se memorează aceste prototipuri se numește listă de invocare a delegării. Evident, se pot apela toate metodele cu aceeași parametrii, ca o facilitate importantă a acestui tip de date. Interfețele corespund tuturor metodelor care modelează structura unei clase.
În cadrul primului exemplu (cel cu definirea clasei) remarcăm keyword-urile public, private. Acestea sunt de fapt niște modificatori de acces, care specifică ce tip de variabile (raportate la clasă) pot accesa respectivul element al clasei.
3. Instrucțiuni de control
Asemenea limbajelor C și C++, limbajul C# conține un flux de execuție, specific fiecărei aplicații. Orice program trebuie să facă ceva. De aceea, sunt necesare unele elemente (instrucțiuni, în acest caz), care să manipuleze fluxul de execuție al programului. Astfel, instrucțiunile sunt de 3 tipuri: instrucțiuni de decizie (if, else-if), instrucțiuni repetitive (while, do-while, for, foreach), instrucțiuni de selecție (switch).
Nu voi descrie fiecare tip de instrucțiune în parte, pentru că nu acesta este scopul tutorialului.
Spre deosebire de C++, limbajul C# dispune de o instrucțiune repetitivă nouă, denumită foreach. Practic, este utilă pentru parcurgerea elementelor unei colecții, spre exemplu, dacă vrem să parcurgem un vector de întregi, scriem așa:foreach(int x in vector)
{
x++;
}
Vector este un tablou unidimensional de tip int. Practic, declarăm o variabilă nouă, denumită x, de tip întreg, care ia pe rând toate valorile din vectorul vector. Apoi, incrementează fiecare element întreg. Remarcăm keyword-ul in, specific acestei instrucțiuni.
Instrucțiunile break și continue: sunt instrucțiuni specifice ciclurilor (instrucțiunilor repetitive). Să analizăm următorul cod:
for(int i=1; i<=10; i++)
{ if(i == 5)
break;
}
Ce se întâmplă aici: avem un for, care iterează de la 1 la 10. În cadrul for-ului avem o instrucțiune de decizie, adică un if, care verifică momentul în care contorul (i-ul) atinge valoarea 5. Atunci când i-ul este 5, se execută instrucțiunea break;, care se traduce prin ”ieșire forțată din ciclu”. Astfel, tot ceea ce ar fi urmat după acest break nu s-ar mai fi executat.
Instrucțiunea continue are ca efect întreruperea iterației curente și începerea iterației imediat următoare.
Cu alte cuvinte:for(int i=1; i<=10; i++)
{
//instructiuni_1
continue;
//instructiuni_2
}
În codul de mai sus, se execută de 10 ori instructiuni_1, iar datorită prezenței instrucțiunii continue; setul de instructiuni_2 nu se va executa niciodată.
Pentru cei interesați, prezint următoarele subiecte de interes:
- operatori în C#
- tipuri valoare, tipuri referință
- directive de preprocesare
- spații de nume în C#
Sper că m-am făcut suficient de înțeles. Pentru nelămuriri, aștept întrebări.
Ioniță Cosmin