Sunteți pe pagina 1din 27

Constructori

• un constructor este un bloc de cod care se executǎ atunci când se utilizeazǎ


cuvîntul cheie new pentru a crea o instanţǎ a unei clase
• rezultatul apelǎrii constructorului îl reprezintǎ crearea (alocarea) unei variabile
de tip referinţǎ
• nu este obligatoriu să se definească un constructor în mod explicit în cadrul unei
clase => atunci când nici un constructor nu este definit în cadrul unei clase,
compilatorul genereazǎ un constructor implicit, ca metodǎ publicǎ
• constructorii trebuie sa aibe acelaşi nume cu clasa însăşi a obiectului
• se definesc şi se utilizează în principiu ca funcţiile membre obişnuite; faţă de
acestea însă, constructorii sunt implicit apelaţi atunci când se crează obiecte ale
clasei respective
• constructorii nu returneazǎ nici un fel de valoare, nici macar de tipul void
• asemănător metodelor, constructorii pot avea multiple definitii, rezultând ceea ce
se numeste supraîncărcarea constructorilor (overloading); deosebirea dintre
aceştia şi selecţia efectivǎ a constructorului apelat la un moment dat este
realizatǎ de cǎtre compilator pe baza numǎrului şi/sau tipul parametrilor actuali
furnizaţi în momentul apelului
• constructorii nu se moştenesc de către descendenti ca alte metode
Constructori- lazy loading
• amânarea creării (alocării) şi inițializării unor obiecte care nu sunt
necesare până la momentul utilizării efective a lor => lazy loading
• alternativa implicită => eager loading => obiectele sunt încărcate în
memorie imediat după crearea lor
• avantaje lazy-loading:
– se minimizeaza timpul de start-up a unei aplicaţii
– aplicaţia consumă mai puţină memorie deoarece încărcarea se
face la solicitare
• implementare .NET: tipul Lazy<T> care poate fi utilizat pentru lazy
loading: o clasă wrapper care furnizează iniţializare lazy pentru orice
clasă
Constructorii impliciţi/fără parametri
internal class Punct
{ public int X { get; set; }
public int Y { get; set; }
}

• se pot obţine:
– prin implementarea explicită de către utilizator al unui constructor
simplu, fără nici un parametru; constructorii fără parametri implementaţi
explicit în cadrul clasei pot include (sau nu!) anumite secvenţe de
instrucţiuni (de regulă, iniţalizări)
//constructor fără parametri implementat explicit
public Punct() { …. }

– fie prin generarea implicită a acestuia de către compilator, atunci când


clasa respectivă nu are implementat explicit nici un constructor
//constructor generat implicit
public Punct() { }
Constructori cu parametri
• iniţializarea câmpurilor/proprietăţilor de date se face pe baza valorilor primite ca
şi parametri de către constructor
public Punct(int x, int y)
{
X = x;
Y = y;
}
• se pot utiliza şi în cazul constructorilor “expression body methods” dacă
implementarea constructorului constă dintr-o singură instrucţiune, constructorii
fiind de fapt un caz particular de metode; în mod particular însă, este permisă în
cazul costructorilor iniţializarea mai multor proprietăţi într-o singură
expresie/instrucţiune folosind sintaxa bazată pe tuple:
public Punct(int x, int y) => (X, Y) = (x, y);

//creare obiect folosind constructorul cu parametri


Punct testpt = new Punct(5,7);
Constructori de copiere
• atunci când se copiază un obiect într-un alt obiect, C# copiază de fapt
referinţa primului obiect în al doilea, ceea ce conduce la crearea a două
referinţe către acelaşi obiect
• dacă se doreşte însă crearea unei instanţe-copii ale unui obiect existent se
poate utiliza un constructor de copiere
• constructorul de copiere primeşte ca parametru un obiect din clasa
respectivă şi crează un al doilea obiect ca o copie a obiectului primit ca
parametru
public Punct(Punct p)
{
this.X = p.X;
this.Y = p.Y;
}
Exemplu
//cream un obiect Punct utilizind constructorul cu paramtri
Punct pt = new Punct(20, 30);
//cream un obiect Punct utilizind constructorul de copiere
Punct pt_copie = new Punct(pt);
//definim o referinta la Punct
Punct pt_alta_copie;
//atribuire de referinte
pt_alta_copie = pt;
//modificam valorile X si Y pentru pt
pt.X = 10;
pt.Y = 15;
Console.WriteLine("Punctul original de coordonate X = "
+ pt.X + " Y = " + pt.Y);
Console.WriteLine("Punctul copiat de coordonate X = {0} Y= {1} ",
pt_copie.X, pt_copie.Y);
Console.WriteLine("Punctul atribuit de coordonate X = {0} Y= {1} ",
pt_alta_copie.X, pt_alta_copie.Y);
Exemplu

pt pt_copie

x 20 x
y 30 y

pt_alta_copie ???
Exemplu – Laboratorul 1
student_01: Student date: Date_univ

nume
prenume
adresa
an_nastere
date_univ

student_01.date_univ = date;
student_02: Student student_02.date_univ = date;
nume
prenume
adresa
an_nastere student_02.date_univ = new Date_univ(date);
date_univ
Finalizers - Destructori
• Finalizers (cunoscuţi anterior ca destructori) sunt utilizaţi în procesul de
distrugere a unei instanţe a unui obiect => efectuează operaţii necesare de
“curăţare” a memoriei
• destructorii nu se moştenesc; de fapt, se poate defini un singur destructor
pentru o clasǎ, fǎrǎ parametri
• destructorii în C# nu pot fi apelaţi în mod explicit, ei sunt apelaţi automat;
• programatorul nu are nici un control asupra momentului cînd destructorul
este apelat pentru cǎ acest lucru este realizat de cǎtre colectorul de deşeuri
(garbage collector) care, dacǎ considerǎ cǎ un obiect poate fi distrus,
apeleazǎ destructorul (dacǎ existǎ).
• Garbage Collector => situațiile în care trebuie implementat un destructor
sunt destul de rare
~Punct()
{
//afisare in fereastra output
System.Diagnostics.Debug.WriteLine
("Acum s-a apelat destructorul clasei Punct!");
}
Destructorii şi metoda Finalize
• destructorii C# sunt convertiţi către un apel la metoda Finalize
(care nu poate fi apelată direct din C#) a clasei System.Object
~Nume_destructor()
{
// cod destructor
}

protected override void Finalize()


{ try
{
//cod destructor
}
finally
{
base.Finalize();
}
}
Finalizers - Destructori
• se va evita definirea de destructori fără cod => apel Garbage Collector =>
penalizare de performanţă => indiferent de faptul ca prezintă sau nu cod
care să realizeze eliberarea de resurse, dacă o clasă prezinta un destructor
(finalizer), atunci colectorul de deşeuri va rezerva un spatiu de memorie şi un
timp de execuţie necesar apelării destructorului
• pentru tipurile de date .NET - tipuri de date standard, sau tipuri proprii
scrise exclusiv folosind C# şi obiecte .NET nu este necesar (contraindicat)
crearea unui destructor
Mecanismul de colectare a deşeurilor
(garbage collection)
• dacǎ nimic dintr-un program nu mai face referire la blocul de memorie iniţial,
acesta devine candidat pentru operaţia de colectare a deşeurilor (sau eliberare a
memoriei neutilizate – garbage collection).
• MicroSoft .NET gestioneazǎ memoria prin intermediul unei rutine de colectare a
deşeurilor: tipurile referinţǎ sunt alocate în memoria heap gestionatǎ de
instrumentul Common Language Runtime CLR.
• rutina de colectare a deşeurilor urmǎreşte utilizarea obiectelor: când nu mai
existǎ referinţe cǎtre un obiect, aceasta va marca memoria utilizatǎ de obiectul
respectiv ca disponibilǎ pentru reutilizare, iar, ulterior, va curǎţa memoria heap şi
va realiza anumite operaţiuni de compactare a acesteia (deplasarea obiectelor
existente în memorie (!))
Mecanismul de colectare a deşeurilor
(garbage collection)
• rutina de colectare a deşeurilor utilizeazǎ un mecanism de tip generaţie în cadrul
cǎruia obiectele care “supravieţuiesc” unei colectǎri sunt trecute într-o generaţie
mai veche.
• folosirea mecanismului de colectare a deşeurilor izoleazǎ programatorul de
detaliile alocǎrii memoriei şi îl scuteşte de responsabilitatea eliberǎrii acesteia
• teoretic, programatorul nu cunoaşte momentul exact cînd rutina de colectare a
deşeurilor va fi lansată în execuţie; practic, se poate forţa lansarea în execuţie a
acesteia printr-un apel explicit la System.GC.Collect; un astfel de apel trebuie
realizat cu precauţie deoarece poate avea consecinţe nedorite asupra vitezei de
execuţie
Tipuri referinţã şi tipuri valoare
Tipuri valoare Tipuri referinţă
Sunt alocate în stivă (stack) Sunt alocate în heap şi sunt distruse
O variabilǎ de tip valoare se creazǎ în mod automat
atunci când se declarǎ o variabilǎ: de către colectorul de deşeuri
• de un tip numeric (int, uint, long, O variabilǎ de tip referinţǎ se creazǎ
double, float …) atunci când se declarǎ:
• de un tip boolean (bool) • un obiect dintr-o anumitǎ clasǎ
inclusiv clasa Object
• de tip enumerare (enum)
• o variabilă de tip interfaţǎ (interface)
• de tip structurǎ (struct)
• un tablou (array)
• un şir (string)
Practic, majoritatea tipurilor simple
• un delegat (delegatul este de fapt
în C# sunt de tip valoare: un obiect care conţine informaţii
bool, char, byte, despre o metodǎ care va fi apelatǎ
la apariţia unui anumit eveniment
sbyte, short, ushort, int, uint,
long, ulong, float, double, • o variabilă referinţă care un referă
decimal. nimic poate fi asignată cu valoarea
null (null reference)
Tipuri referinţã şi tipuri valoare
Tipuri nullable (Nullable types)
• Tipuri valoare nullable (nullable value types) => în versiunile iniţiale ale limbajului
C# nu era posibil ca un tip valoare să fie assignat cu valoarea null, acest lucru fiind
posibil doar pentru tipurile de tip referinţă => în anumite situaţii acest lucru poate
simplifica codificarea (fisiere XML sau JSON).
• pentru tipurile valoare nullable, Nullable nu adaugă overhead-ul aferent unui tip
referinţă (stocarea în heap, garbage collection) fiind în esenţă un struct.
int? x1 = null;
echivalent cu:
Nullable<int> x2 = null;
Tipuri nullable (Nullable types)
• Tipuri referinţă nullable (nullable reference types) => datorită faptului că multe
excepţii în aplicaţiile .NET erau de tipul NullReferenceException, începând cu
versiunea C# 8.0 au fost introduse aşa numitele tipuri referinţă nullable prin care se
evită aruncarea unor astfel de excepţii deoarece se verifică în prealabil valoarea
referinţei
string? s1 = null;
//aici nu se obtine exceptie!!
string? s2 = s1?.ToUpper();
echivalent cu:
string? s1 = null;
//verificare referinta s1 pentru a nu se genera o exceptie
if (s1 is not null) s2=s1.ToUpper();
• dacă în cazul tipurilor valoare se utilizează tipul Nullable<T>, în cazul tipurilor
referinţă compilatorul adaugă adnotării pentru tipurile nullable => atributul
Nullable asociat.
• setarea opţiunii de a avea tipuri referinţă nullable se poate realiza din fişierul
asociat proiectului: <Nullable>enable</Nullable>
Obiecte (tipuri) immutable
• un obiect (tip) imutabil este un obiect a cǎrui stare nu poate fi modificatǎ =>
un obiect de tip imutabil conţine doar membri a căror stare (valoare) nu
poate fi modificată după ce aceasta a fost iniţializată
• câmpurile unui obiect imutabil sunt de obicei declarate readonly şi sunt
inițializate prin constructor la crearea obiectului
• dupǎ aceastǎ inițializare valorile acestora nu mai pot fi modificate
• modificarea valorilor obiectului implicǎ de fapt crearea şi alocarea unui nou
obiect
• exemplu: tipul string
https://learn.microsoft.com/en-us/dotnet/api/system.string?view=net-7.0
Membri de tip const si readonly
• ȋn cadrul unei clase, pe lȃnga cȃmpurile de date variabile, mai pot exista
cȃmpuri de date constante const sau câmpuri de tip readonly
• constante (const) = nu ȋşi modifica valoarea
• readonly = valoarea constantă a câmpului respectiv nu este cunoscută şi nu
poate fi iniţializată de la început ca o constantǎ => iniţializarea unui cȃmp
readonly se poate face doar ȋntr-un constructor

internal class Persoana


{
//camp de date readonly
readonly DateTime data_nasterii;
//camp de date constant
const string CNP = "2780512054672";
//initializare camp date readonly - constructor
public Persoana(DateTime dn)
{ data_nasterii = dn; }
}
Clasa Object (System)
• Equals - indică faptul că două instanţe de tip Object sunt egale.
Implementarea implicită testează însă doar egalitatea referinţei (şi returnează
true dacă ambele referinţe punctează către acelaşi obiect => apelează
ReferenceEquals; pentru tipurile valoare este testată egalitatea la nivel de bit
=> clasele derivate trebuie să redefinească metoda pentru a-şi defini propria
modalitate de testare a egalităţii pentru obiectele proprii
• GetHashCode – utilizată pentru a implementa o funcţie de dispersie
(hashing) care returnează o valoare (cod) de hashing pentru obiectul curent;
de cele mai multe ori, mai multe câmpuri sunt implicate în calculul acesteia
(de exemplu SAU exclusive -XOR între acestea) => se generează un număr,
care ar putea fi utilizat în cadrul unei tabele de dispersie
• GetType - returnează tipul tipul curent, la rulare, al instanței curente
(de tipul Type)
• ReferenceEquals - determină dacă două referinţe indică spre aceleaşi obiect
• ToString - returnează un String care este reprezentarea sub formă de şir de
caractere a obiectului curent
• MemberwiseClone – creaza o copie superficială (shallow) a obiectului curent
https://learn.microsoft.com/en-
us/dotnet/api/system.object.memberwiseclone?view=net-7.0
Mecanismul împachetare/despachetare
(boxing/unboxing)
• împachetarea reprezintǎ transformarea unui tip valoare în tip referinţǎ,
creînd un nou obiect în memoria heap gestionatǎ care are aceleaşi cîmpuri
ca şi tipul valoare; instrumentul Common Language Runtime (CLR) creazǎ
un astfel de obiect şi îl iniţializeazǎ cu cîmpurile şi valorile din tipul valoare.
int i = 10;
i.ToString();
• compilatorul crează o instanţă a clasei Object care conţine valoarea
întreagă i (împachetare implicită). Apoi se realizează apelul către metoda
ToString folosindu-se instanţa creată a obiectului
int i = 10;
Object obj = i;
obj.ToString();
• despachetarea implicǎ operaţia inversǎ: adicǎ crearea unui tip valoare
dintr-un tip referinţǎ; despachetarea însă presupune transformarea
explicită dintr-un obiect într-un tip valoare:
int j = (int)obj;
Exemplu
//definire struct – tip valoare
public struct Valoare
{
public Valoare(int x, int y) { this.x = x; this.y = y; }
public int x { get; set; }
public int y { get; set; }
}

Valoare a = new Valoare(1,2);


//boxing - impachetare
Object o = a;
Console.WriteLine("Tipul variabilei o este" + o.GetType());
Console.WriteLine("Tipul variabilei a este" + a.GetType());
//unboxing - despachetare
if ((((Valoare)o).x == a.x) && (((Valoare)o).y == a.y))
Console.WriteLine("Impachetare/despachetare reusita!");
Mecanismul împachetare/despachetare
(boxing/unboxing)
• obiectul împachetat acţioneazǎ ca un container în cadrul cǎruia se
regaseşte o copie a tipului valoare pe care îl împacheteazǎ;
utilizarea funcţiei GetType() va returna acelaşi tip atât pentru
obiectele neîmpachetate cât şi pentru cele împachetate.
• în general, GetType() returneazǎ un obiect de tipul Type, o clasǎ
definitǎ în spaţiul de nume System.GetType()
• clasa Type permite obţinerea de informaţii despre un anumit
obiect, inclusiv metodele, cîmpurile de date şi proprietǎţile
acestuia
• operatorul typeof returneazǎ de asemenea un obiect de tipul
Type; diferenţa dintre cele douǎ constǎ din aceea cǎ GetType()
este aplicatǎ unui obiect iar typeof se aplicǎ unei clase
Împachetarea şi
conversiile de tip
• împachetarea şi despachetarea reprezintă transformări între tipul
valoare şi referinţă (obiect) în timp ce conversiile de tip transformă
tipul (aparent) al obiectului
• tipurile valoare sunt memorate în stivă în timp ce obiectele sunt
memorate în heap
• împachetarea preia o copie a tipului valoare de pe stivă în heap iar
despachetarea repune tipul valoare pe stivă
• conversiile de tip pe de altă parte nu realizează nici un fel de mutări
fizice ale obiectelor, doar schimbă modul în care acestea sunt
tratate de program prin modificarea tipului referinţei
Clase partiale
• ȋn mod tipic, o clasă rezidă ȋn general ȋntr-un singur fişier
• utilizȃnd partial este posibilă crearea unor clase (valabil şi pentru
structuri, metode sau interfeţe) care să se ȋntindă pe mai multe fişiere =>
compilatorul va genera ȋn final o singură clasă, cu conţinutul comun al
claselor parţiale, utilizarea membrilor acestora realizȃndu-se ca şi cȃnd
aceştia ar rezida ȋn acelaşi fişier

internal partial class Clasa_partiala


{
public void Metoda1()
{
Console.WriteLine("Metoda 1!");
}
Clasa_partiala c = new Clasa_partiala();
}
c.Metoda1();
//in alt fisier
c.Metoda2();
internal partial class Clasa_partiala
{
public void Metoda2()
{
Console.WriteLine("Metoda 2!");
}
}
Tablouri in C# (1)
Tablouri unidimensionale
int[] tab_uni = new int[2];
tab_uni[0] = 1;
tab_uni[1] = 2;
// alte posibilitati de initializare a tabloului
int[] tab_uni = new int[] { 1, 2 };
int[] tab_uni = { 1, 2 };

Obs. variabila tablou creată dispune de o serie de metode (clasa System.Array) prin care
se pot realiza diferite operaţii asupra elementelor tabloului.
Console.WriteLine( "Dimensiunea tabloului =
"+tab_uni.GetLength(0));

Tablourile multidimensionale rectangulare - reprezintă tablouri care au mai multe


dimensiuni fixate prin declaraţia tabloului :
//declarare tablou multidimensional rectangular 2x3 fara initializare
int[,] tab_bi_0 = new int[2, 3];
//declarare tablou bidimensional cu initializare
int[,] tab_bi = { { 1, 2, 3 }, { 4, 5, 6 } };
for (int i = 0; i < tab_bi.GetLength(0); i++)
for (int j = 0; j < tab_bi.GetLength(1); j++)
Console.WriteLine(tab_bi[i, j]);
Tablouri in C# (2)
Tablourile multidimensionale (jagged) - sunt de fapt tablouri multidimesionale dar cu
dimensiuni neregulate. Flexibilitatea în acest caz derivă din aceea că tablourile sunt
imlementate ca tablouri de tablouri

//creare tablou jagged fara initializare


int[][] tab_jag = new int[2][];
tab_jag[0] = new int[4];
tab_jag[1] = new int[6];

//creare tablou jagged cu initializare


int[][] tab_jag = new int[][]
{ new int[] { 1, 2, 3, 4 },
new int[] { 5, 6, 7, 8, 9, 10 } };

//accesare elemente tablou utilizind Length


for (int i = 0; i < tab_jag.Length; i++)
for (int j = 0; j < tab_jag[i].Length; j++)
Console.WriteLine(tab_jag[i][j]);

S-ar putea să vă placă și