Sunteți pe pagina 1din 36

1

Curs 2

Clase

Materialul de fata se bazeaza pe:
Inside C# Second Edition, T. Archer
MSDN

O clasa poate fi definita ca incapsularea datelor si metodelor ce lucreaza pe aceste date.
Definirea claselor. Sintaxa:

[attributes] [modifiers] class <className> [: baseClassName]
{
[corp clasa]
}[;]

Exemplu:

class Student
{
private long IdStudent;
}

Contine o singura data membru, IdStudent, ce are modificatorul de acces private.

Membrii clasei

In continuare prezentam lista tipurilor ce o putem defini ca membri pentru o clasa C#.

Campuri (Fields)
Un camp (field) este o variabila membru folosita pentru a pastra o valoare. Pentru un camp se
pot aplica mai multi modificatori: static, readonly si const.

Metode
O metoda contine codul ce actioneaza pe datele obiectului.

Proprietati
Proprietatile mai sunt cunoscute si sub numele de smart fields pentru ca ele sunt metode, iar
de client sunt vazute drept campuri.

Constante
O constanta este un camp cu o valoare ce nu poate fi schimbata.

Indexeri
Un indexer este referit ca smart array; permite sa lucram cu clase ce reprezinta in mod logic
un tablou de date.
Pentru a indexa datele clasei, proiectantul clasei are doua posibilitati: fie permite accesul
direct la datele interne ale clasei (date reprezentate sub forma de tablou), fie furnizeaza
metode ce au ca parametri un index ce defineste elementul din tablou ce va fi folosit.
2



Evenimente
Un eveniment are ca efect executia unui cod. SO Windows se bazeaza pe evenimente. In
general evenimentele genereaza mesaje.

Operatori
In C# putem supraincarca operatorii matematici.


Modificator de acces Descriere
public
Membrul este accesibil din afara definitiei clasei si a ierarhiei
claselor derivate.
protected
Membrul nu este vizibil in afara clasei si poate fi accesat
numai de clasele derivate.
private
Membrul nu este vizibil in afara clasei si nici in clasele
derivate.
internal
Membrul este vizibil numai in interiorul unitatii curente de
compilare, numai din fisierele din acelasi assembly. Acest
modificator creaza un hibrid intre public si protected, totul
depinzind in ultima instanta de locul unde se afla codul. O
utilizare comuna a acestui modificator este in dezvoltarea
bazata pe componente pentru ca permite unui grup de
componente sa coopereze intr-un mod privat.

protected internal *
Accesul este limitat la assembly-ul curent sau la tipurile
derivate din clasa continuta.

Implicit modificatorul de acces este private.

Membru in Accesibilitate
implicita
Permite modificarea accesibilitatii
enum public -
class private public
protected
internal
private
protected internal
interface public -
struct private public
internal
private

In C# trebuie sa indicam pentru fiecare data membru modificatorul de acces.
In C++ putem scrie:

class Cpp
{
public:
3
int a;
int b;
int c;
protected:
int d;
int e;
};

iar in C# acelasi lucru se descrie astfel:

class CSharp
{
public int a;
public int b;
public int c;
protected int d;
protected int e;
}

Exemple:

modificator de acces protected

class A
{
protected int x = 123;
}

class B : A
{
void F()
{
A a = new A();
B b = new B();
a.x = 10; // Eroare
b.x = 10; // OK
}
}


modificator de acces: internal

Exemplul (vezi MSDN) urmator contine doua fisiere, Assembly1.cs si Assembly2.cs. In
primul fisier se declara o clasa de baza, BaseClass ca fiind internal, in cel de-al doilea fisier se
incearca sa se acceseze membrul din clasa de baza.
Fiser Assembly1.cs:
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int IntM = 0;
}
Fisier Assembly2.cs
// Assembly2.cs
// compile with: /reference:Assembly1.dll
// CS0122 expected
class TestAccess
{
4
public static void Main()
{
BaseClass myBase = new BaseClass();
// eroare, BaseClass nu este vizibila in afara assembly
}
}


Domeniul de accesibilitate
Domeniul de accesibiltate a unui membru specifica unde, in sectiunile programului, un
membru poate fi referit.

Urmatorul exemplu contine tipul T1, si doua clase imbricate M1 si M2.



// cs_Accessibility_Domain.cs
using System;
namespace MyNameSpace
{
public class T1
{
public static int myPublicInt;
internal static int myInternalInt;
private static int myPrivateInt = 0;

public class M1
{
public static int myPublicInt;
internal static int myInternalInt;
private static int myPrivateInt = 0;
}

private class M2
{
public static int myPublicInt = 0;
internal static int myInternalInt = 0;
private static int myPrivateInt = 0;
}
}

public class MainClass
{
public static int Main()
{
// Access to T1 fields:
T1.myPublicInt = 1; // Accesul este nelimitat
T1.myInternalInt = 2; // Accesibil numai in assembly curent
// T1.myPrivateInt = 3; // Eroare: inaccesibil in afara lui T1

// Acces la campurile din M1:
T1.M1.myPublicInt = 1; // Acces nelimitat
T1.M1.myInternalInt = 2; // Accesibil numai in assembly curent
//T1.M1.myPrivateInt = 3;//Eroare: inaccesibil in afara lui M1

// Acces la campurile lui M2:
// T1.M2.myPublicInt = 1; // Eroare: inaccesibil in afara lui T1
// T1.M2.myInternalInt = 2; // Eroare: inaccesibil in afara lui T1
// T1.M2.myPrivateInt = 3; // Eroare: inaccesibil in afara lui M2

return 0;
5
}
}
}




Memento
Modificatorii de clasa
Fiecare instructiune executabila trebuie plasata in interiorul unei clase sau structuri. Clasele
definesc tipul referinta ce sta la baza construirii blocurilor de instructiuni din C#.

O clasa poate fi ...

abstract: O instanta a clasei nu poate fi creata. Aceasta serveste ca o clasa de baza.
sealed: clasa nu poate fi folosita in procesul de derivare, altfel spus nu poate fi clasa de baza
pentru o alta clasa. O clasa nu poate fi abstracta si sealed.
internal: clasa este accesibila numai din alte clase din cadrul aceluiasi assembly. Acesta
constituie accesul implicit pentru tipuri ne-cuibarite. Daca nu se specifica nici un modificator,
implicit este internal.
new: folosit numai cu clase imbricate. "New" indica ca clasa ascunde un membru mostenit cu
acelasi nume.
private: O clasa incuibarita ce poate fi accesta numai in interiorul clasei unde este definita.
public: Instantele acestei clase sunt disponibile oricarei clase ce doreste sa o acceseze.

Constructori

O clasa defineste date membru, metode si tipuri incuibarite (nested types). Un ctor este o
metoda speciala folosita in mod normal pentru a initializa datele membru ale clasei, si are
acelasi nume ca si clasa. Nu returneaza valori. Pot fi definiti mai multi ctori in cadrul unei
clase. Daca nu s-a definit nici un ctor, compilatorul C# furnizeaza un ctor implicit fara
parametri.

In C# obiectele se creaza numai folosind cuvintul cheie new.

C# introduce un nou tip de constructor numit ctor static.
Acest lucru se datoreaza faptului ca .NET a implementat un mecanism de gestiune a memoriei
cunoscut sub numele de garbage collection.

using System;
class CtorApp
{
CtorApp()
{
Console.WriteLine("[CtorApp] " + "S-a apelat ctor din CtorApp");
}

public static void Main()
{
Console.WriteLine("\n[Main] Instantiem un obiect CtorApp ");
CtorApp app = new CtorApp();
}
}
6

Obiectele sunt instantiate folosind operatorul new ce are urmatoarea sintaxa:

<class> <object> = new <class>(constructor arguments)

In C++ putem crea obiectele pe stiva sau in heap.
In C# instantierea obiectelor este diferita. Locul unde va fi creat obiectul depinde de tipul ce
va fi instantiat (tip valoare creat pe stiva, tip referinta creat in heap).
Operatorul new creaza o noua instanta a clasei, dar nu determina locul unde va fi creat
obiectul.

De exemplu, o declaratie de tipul:

MyClass myClass;

semnifica in C# ca myClass este o variabila de tipul MyClass, dar nu instantiaza obiectul
(ctor-ul nu este apelat). Instantierea se face cu operatorul new, ca in mai jos :

myClass = new MyClass();

Declararea unei variabile de tipul MyClass se foloseste atunci cind dorim sa reutilizam clasa
MyClass in interiorul altei clase. Imbricarea de clase se numeste containment sau agregare.

Membri statici si membri ai instantei

Putem defini un membru al unei clase ca fiind static sau membru al instantei (instance
member). Implicit fiecare membru al unei clase este instance member ceea ce inseamna ca
o copie a acestui membru este facuta pentru fiecare instanta a clasei.
Pentru un membru static exista o singura copie pentru fiecare instanta a clasei.
Un membru static este creat cand aplicatia ce contine clasa este incarcata si exista atita timp
cat exista aplicatia.
Putem accesa un membru static inainte ca clasa sa fie instantiata.
De exemplu metoda Main, ce constituie punctul de intrare in aplicatie, trebuie declarata ca
fiind statica. De asemenea daca dorim sa monitorizam cate instante ale unui obiect sunt create
pe durata de existenta a aplicatiei.
Un membru static trebuie sa aiba o valoare valida si poate fi initializata in momentul
declararii.

using System;
class InstCount
{
public InstCount()
{
instanceCount++;
}
static public int instanceCount;
// instanceCount = 0;
}
class AppClass
{
public static void PrintInstanceCount()
{
Console.WriteLine("[PrintInstanceCount] Now there {0} " +
7
"{1} instance{2} of the InstCount class",
InstCount.instanceCount == 1 ? "is" : "are",
InstCount.instanceCount,
InstCount.instanceCount == 1 ? "" : "s");
}
public static void Main()
{
PrintInstanceCount();

InstCount ic;
for (int i = 0; i < 2; i++)
{
ic = new InstCount();
Console.WriteLine("[Main] Instantiated a " +
"{0} object...", ic.GetType());
PrintInstanceCount();
}
}
}

Initializarile constructorilor

Toti ctorii obiectelor C# (exceptie ctor System.Object radacina) includ o invocare a ctorilor
clasei de baza inainte de invocarea ctorului obiectului curent.
Aceste initializari ne permit sa specificam ce clasa si ce ctor dorim sa apelam.
Exista doua forme :

base()

ce permite sa apelam ctor clasei de baza (legat de numarul de parametri si tipul acestora).

this()

permite clasei curente sa apeleze un alt constructor.

Exemplu

using System;
class BaseClass
{
public BaseClass()
{
Console.WriteLine("[BaseClass.BaseClass] " +
"Constructor called");
}
}
class DerivedClass : BaseClass
{
public DerivedClass()
{
Console.WriteLine("[DerivedClass.Derived] " +
"Constructor called");
}
}
class DefaultInitializer
{
public static void Main()
8
{
Console.WriteLine("[Main] Instantiating a " +
"DerivedClass object");
DerivedClass derived = new DerivedClass();
}
}

Acelasi lucru ca mai sus, dar in clasa derivata la ctor se apeleaza base(...):
Efectul este acelasi.

using System;
class BaseClass
{
public BaseClass()
{
Console.WriteLine("[BaseClass.BaseClass] " +
"Constructor called");
}
}
class DerivedClass : BaseClass
{
public DerivedClass()
: base()
{
Console.WriteLine("[DerivedClass.Derived] " +
"Constructor called");
}
}
class BaseInitializer1
{
public static void Main()
{
Console.WriteLine("[Main] Instantiating a " +
"DerivedClass object");
DerivedClass derived = new DerivedClass();
}
}

using System;
class BaseClass
{
public BaseClass()
{
Console.WriteLine("[BaseClass.BaseClass()] " +
"Parameterless constructor called");
}
public BaseClass(int foo)
{
Console.WriteLine("[BaseClass.BaseClass(int foo)] " +
"foo = {0}", foo);
}
}
class DerivedClass : BaseClass
{
public DerivedClass(int foo)
{
Console.WriteLine("[DerivedClass.DerivedClass] " +
"foo = {0}", foo);
}
}
9
class BaseInitializer2
{
public static void Main()
{
Console.WriteLine("[Main] Instantiating a " +
"DerivedClass object...");
DerivedClass derived = new DerivedClass(42);

}
}

using System;

class BaseClass
{
public BaseClass()
{
Console.WriteLine("[BaseClass.BaseClass()] " +
"Parameterless constructor called");
}
public BaseClass(int foo)
{
Console.WriteLine("[BaseClass.BaseClass(int foo)] " +
"foo = {0}", foo);
}
}

class DerivedClass : BaseClass
{
public DerivedClass(int foo) : base(foo)
{
Console.WriteLine("[DerivedClass.DerivedClass] " +
"foo = {0}", foo);
}
}

class BaseInitializer3
{
public static void Main()
{
Console.WriteLine("[Main] Instantiating a " +
"DerivedClass object...");
DerivedClass derived = new DerivedClass(42);

}
}

10

Specificarea informatiei la run-time in lista de initializare a constructorului

Probleme legate de membrii statici si membrii instanta in initializarea constructorilor.
In C# numai membrii statici pot fi utilizati, in lista de initializare, cand se apeleaza
constructorii din clasa de baza. Mai exact, daca ctor din clasa derivata nu are parametri iar
ctor din clasa de baza are parametri atunci in lista de initializare trebuie specificati membri
statici.

Vezi urmatorul exemplu:

class MyBase
{
protected MyBase(int i) { }
};

class MyDerived : MyBase
{
int i;

// ERROR: The compiler states that the following will not
// compile because an object reference is needed for
// nonstatic fields. However, if you qualify the i parameter
// with the this value--base(this.i)--you will still receive
// an error, this time indicating that the this keyword cannot
// be used in this context (constructor initializer).
MyDerived() : base(i) { }
};

O alta incercare de rezolvare a problemei ar fi:

class CommaDelimitedFile
{
public CommaDelimitedFile(string fileName)
{
}
}

enum TableId
{
Customers,
Suppliers,
Vendors
};

class DbTable : CommaDelimitedFile
{
public DbTable(TableId tableId)
{
}
}

Eroarea este de acelasi tip, nu exista ctor fara parametri.

Rezolvarea consta in a folosi o metoda statica in lista de initializare a ctorului.
Vezi codul:

11
using System;
class CommaDelimitedFile
{
public CommaDelimitedFile(string fileName)
{
Console.WriteLine("[CommaDelimitedFile." +
"CommaDelimitedFile] file name = {0}", fileName);
}
}

enum TableId
{
Customers,
Suppliers,
Vendors
};

class DbTable : CommaDelimitedFile
{
static string GetFileName(TableId tableId)
{
string fileName;

switch(tableId)
{
case TableId.Customers:
fileName = "customers.txt";
break;

case TableId.Suppliers:
fileName = "suppliers.txt";
break;

case TableId.Vendors:
fileName = "venders.txt";
break;

default:
throw new ArgumentException("[DbTable." +
"GetFileName] Could not resolve table name");
}

return fileName;
}

public DbTable(TableId tableId) : base(GetFileName(tableId))
{
Console.WriteLine("[DbTable.DbTable] tableId = {0}", " +
tableId.ToString());
}
}

class BaseInitializer4
{
public static void Main()
{
Console.WriteLine("[Main] Instantiating a " +
"Customer Table object...");
DbTable derived = new DbTable(TableId.Customers);
}
}
12

Metode
Metodele sunt totdeauna definite in interiorul clasei sau structurii.
Metodele pot fi:
instance (apelata ca o instanta a tipului in interiorul caruia metoda a fost definita)
sau
static, unde metoda este asociata cu tipul insusi.
Metodele pot fi declarate ca virtual, abstract , sau sealed.
Metodele pot fi supraincarcate, suprascrise si ascunse (overloaded, overridden and hidden).

Modificatorii de acces pentru metode

Sunt folositi ca parti in sintaxa de declarare a metodelor si pot fi:

internal
private
protected
protected internal (Microsoft spune ca este nivel de acces)
public

Implicit se considera acces privat.

public indica ca o metoda este accesibila atit in interiorul cat si in exteriorul clasei in care a
fost definita.
internal inseamna ca metoda este accesibila numai tipurilor definite in acelasi assembly.
protected metoda este accesibila in tipul in care a fost definita si in tipurile derivate din
acest tip. Acesta este folosit pentru a da accesul claselor derivate la metodele din clasa de
baza.
protected internal metoda este accesibila tipurilor definite in acelasi assembly sau tipurilor
dintr-un assembly derivat.
private metodele sunt accesibile numai in calsa unde au fost definite.
metodele sealed sunt metode care suprascriu o metoda virtuala mostenita avand aceeasi
signatura. Cand o metoda este sealed, aceasta nu poate fi suprascrisa intr-o clasa derivata.



Modificutor de
ucces
Restrictii
public
Mici o resfricfie. AfribufeIe si mefodeIe morcofe cu public sunf vi;ibiIe oricorei
mefode din orice cIoso.
private
AfribufeIe si mefodeIe din cIoso A core sunf morcofe co private sunf occesibiIe
numoi mefodeIor din cIoso A.
protected
AfribufeIe si mefodeIe din cIoso A core sunf morcofe co protected sunf occesibiIe
mefodeIor din cIoso A precum si mefodeIor din cIoseIe derivute din cIoso A.
internal
AfribufeIe si mefodeIe din cIoso A core sunf morcofe co internal sunf occesibiIe
mefodeIor din orice cIoso o ossembIy A.
13
protected
internal
AfribufeIe si mefodeIe din cIoso A core sunf morcofe co protected internal
sunf occesibiIe mefodeIor cIosei A, mefodeIor cIoseIor derivofe din A si de
osemeneo oricorei cIose din ossembIy A. Acesfo esfe in mod efecfiv profejof.

Accesul membrilor

Accesibilitate declarata: public, protected, internal, protected internal, private.

Namespaces sunt implicit public. Nu sunt permisi modificatori de acces pentru namespace.

Tipurile declarate in unitatile de compilare sau namespaces pot avea accesibilitatea public sau internal
(implicit internal).
Membrii clasei pot avea accesibilitatea: public, protected, internal, protected internal, private.
Membrii interfetei au implicita accesibilitatea public. Nu este permisa modificarea accesibilitatii.

Parametrii transmisi prin valoare si referinta
Valoarea parametrilor transmisi prin valoare ramine neschimbata in programul apelant.
Modificarile valorilor parametrilor transmisi prin referinta sunt vazute in programul apelant.
Vedeti urmatoarele exemple:

class SomeClass
{
public int ChangeInt(int val)
{
return val*2;
}
}

class ValRefTest
{
static void Main(string[] args)
{
SomeClass sc = new SomeClass();
int val1 = 3;
int val2 = sc.ChangeInt(val1);
Console.WriteLine("val1 = {0}, val2 = {1}",
val1, val2);
}
}
Iesirea va fi:
val1 = 3, val2 = 6
Cand folosim parametri tip referinta se transmite o copie a referintei (o alta referinta la acceasi
data).
Vezi urmatorul cod:
class AnotherClass
{
14
public int ID;
}
class SomeClass
{
public AnotherClass ChangeObject(AnotherClass ref1)
{
ref1.ID = ref1.ID*2;
return ref1;
}
}
class ValRefTest
{
static void Main(string[] args)
{
AnotherClass ref1 = new AnotherClass();
ref1.ID = 3;
AnotherClass ref2 = sc.ChangeObject(ref1);
Console.WriteLine("ref1.ID = {0}, ref2.ID = {1}",
ref1.ID, ref2.ID);
}
}
Iesirea va fi:
ref1.ID = 6, ref2.ID = 6
In ambele cazuri se transmite o copie a parametrilor din programul apelant catre programul
apelat. In primul caz se transmite o copie a valorii, iar in al doilea caz se transmite o copie a
referintei.

ref ca parametrii ai metodei
Cuvantul cheie ref spune compilatorului C# ca argumentele pasate puncteaza la aceeasi
memorie ca si variabilele din codul apelant. Daca metoda apelata modifica valorile,
modificarile sunt vazute in codul apelant.
Vezi exemplul :
class Color
{
public Color()
{
this.red = 0;
this.green = 127;
this.blue = 255;
}

protected int red;
protected int green;
protected int blue;

public void GetRGB(ref int red, ref int green, ref int blue)
{
red = this.red;
green = this.green;
blue = this.blue;
15
}
}

class Class1
{
static void Main(string[] args)
{
Color c = new Color();
int red;
int green;
int blue;
c.GetRGB(ref red, ref green, ref blue);
Console.WriteLine("R={0}, G={1}, B={2}",
red, green, blue);
}
}
Restrictie:
Cand folosim ref, trebuie sa initializam argumentele inainte de a le folosi ca argumente ale
functiei.
Exemplu:

class Color
{
public Color()
{
this.red = 0;
this.green = 127;
this.blue = 255;
}

protected int red;
protected int green;
protected int blue;

public void GetRGB(
ref int red, ref int green, ref int blue)
{
red = this.red;
green = this.green;
blue = this.blue;
}
}

class Class1
{
static void Main(string[] args)
{
Color c = new Color();
int red = 0;
int green = 0;
int blue = 0;
c.GetRGB(ref red, ref green, ref blue);
Console.WriteLine("R={0}, G={1}, B={2}",
red, green, blue);
}
}
16
Iesirea va fi:
R=0, G=127, B=255

out ca parametrii ai metodei
Are acelasi efect ca si ref, dar diferenta principala este ca nu e necesara initializarea
parametrilor inainte de apel.
Exemplu:

class Color
{
public Color()
{
this.red = 0;
this.green = 127;
this.blue = 255;
}

protected int red;
protected int green;
protected int blue;

public void GetRGB(out int red, out int green,
out int blue)
{
red = this.red;
green = this.green;
blue = this.blue;
}
}

class Class1
{
static void Main(string[] args)
{
Color c = new Color();
int red;
int green;
int blue;
c.GetRGB(out red, out green, out blue);
Console.WriteLine("R={0}, G={1}, B={2}",
red, green, blue);
}
}
Parametrii prefixati cu out trebuiesc modificati in metoda apelata, iar parametrii prefixati cu
ref pot fi modificati.
Vezi urmatoarele doua exemple:

class Color
{
public Color()
{
this.red = 0;
this.green = 127;
17
this.blue = 255;
}

protected int red;
protected int green;
protected int blue;

public void GetRGB(ref int red, ref int green,
ref int blue)
{
// red = this.red;
// green = this.green;
// blue = this.blue;
}

}

class Class1
{
static void Main(string[] args)
{
Color c = new Color();
int red = 0;
int green = 0;
int blue = 0;
c.GetRGB(ref red, ref green, ref blue);

Console.WriteLine("R={0}, G={1}, B={2}",
red, green, blue);
}
}
However, this code wont compile because the red, green, and blue parameters must be
assigned in the GetRGB method:
class Color
{
public Color()
{
this.red = 0;
this.green = 127;
this.blue = 255;
}

protected int red;
protected int green;
protected int blue;

public void GetRGB(out int red, out int green,
out int blue)
{
// red = this.red;
// green = this.green;
// blue = this.blue;
}

}

class Class1
{
18
static void Main(string[] args)
{
Color c = new Color();
int red;
int green;
int blue;
c.GetRGB(out red, out green, out blue);

Console.WriteLine("R={0}, G={1}, B={2}",
red, green, blue);
}
}
19
Supraincarcarea metodelor
Aceleasi nume de metoda dar cu parametri diferiti. Nu are importanta daca returneaza tipuri
diferite si nu au aceeasi modificatori de acces.
Exemplu:

class Log
{
public Log(string fileName)
{
// Open fileName and seek to end.
}

public void WriteEntry(string entry)
{
Console.WriteLine(entry);
}

public void WriteEntry(int resourceId)
{
Console.WriteLine(
"Retrieve string using resource id and write to log");
}
}

class Class1
{
static void Main(string[] args)
{
Log log = new Log("My File");
log.WriteEntry("Entry one");
log.WriteEntry(42);
}
}
Urmatoarele metode sunt considerate supraincarcari corecte:
public void WriteEntry(string entry, int resourceId)
{
Console.WriteLine(entry + " " + resourceId);
}

public void WriteEntry(int resourceId, string entry)
{
Console.WriteLine(resourceId + " " + entry);
}
In timp ce modificatorii ref si out sunt suficienti pentru a face diferenta de la o versiune la alta
a unei metode, compilatorul considera ref si out echivalenti. Din acest motiv metodele
urmatoare sunt corecte, dar nu luate impreuna (ori una, ori alta):
// Either of these is OK, but not both:
public void WriteEntry(ref string entry)
{
Console.WriteLine(entry);
}

20
public void WriteEntry(out string entry)
{
entry = "Foo";
Console.WriteLine(entry);
}

Supraincarcarea constructorilor
Daca scriem un constructor cu parametri, compilatorul nu mai genereaza constructorul
implicit fara parametri, si deci urmatorul cod nu este corect:
class File
{
}

class CommaDelimitedFile
{
public CommaDelimitedFile(String fileName)
{
Console.WriteLine("Constructed with a file name");
}

public CommaDelimitedFile(File file)
{
Console.WriteLine("Constructed with a file object");
}
}

class Class1
{
static void Main(string[] args)
{
File file = new File();
CommaDelimitedFile file2 =
new CommaDelimitedFile(file);
CommaDelimitedFile file3 =
new CommaDelimitedFile("Some file name");
}
}
Nu putem declara CommaDelimitedFile astfel:
CommaDelimitedFile file4 =
new CommaDelimitedFile();
Daca suprimam constructorii definiti, atunci compilatorul va genera ctor implicit fara
parametri si urmatorul cod va fi corect:
class File
{
}

class CommaDelimitedFile
{
/* public CommaDelimitedFile(String fileName)
{
Console.WriteLine("Constructed with a file name");
}
21

public CommaDelimitedFile(File file)
{
Console.WriteLine("Constructed with a file object");
}
*/
}

class Class1
{
static void Main(string[] args)
{
File file = new File();
// CommaDelimitedFile file2 =
// new CommaDelimitedFile(file);
// CommaDelimitedFile file3 =
// new CommaDelimitedFile("Some file name");

CommaDelimitedFile file4 =
new CommaDelimitedFile();
}
}
Problema de mai sus se rezolva folosind cuvintul cheie this.
Vezi urmatorul cod:

public CommaDelimitedFile(String fileName) : this()
si expresia this() rezolva un apel la:
public CommaDelimitedFile()
{}
care trebuie sa fie disponibil.
Putem folosi o lista de initializare a constructorului pentru a apela explicit un constructor din
alt constructor:
public CommaDelimitedFile() : this("Default string")
{}

O metoda este considerata supraincarcata si in cazul cind exista versiuni diferite ale acesteia
numai in clasa de baza.
Exemplu:

class Log
{
public Log(string fileName) {}
public Log() {}

public void WriteEntry(string entry)
{
Console.WriteLine(entry);
}
22
}

class LogEx : Log
{
public LogEx(string fileName) {}

public void WriteEntry(int resourceId)
{
Console.WriteLine(
"Retrieve resource id and write to log");
}
}

class Class1
{
static void Main(string[] args)
{
LogEx log = new LogEx("My File");
log.WriteEntry("Entry one");
log.WriteEntry(42);
}
}
Iesirea va fi:
Entry one
Retrieve resource id and write to log
23

Mostenire. Metode virtuale

Suprascrierea metodelor
Sa analizam urmatorul exemplu, atentia fiind indreptata catre functia CalculatePay.
class Employee
{
public void CalculatePay()
{
Console.WriteLine("Employee.CalculatePay()");
}
}
Acum derivam o clasa din Employee si suprascriem metoda CalculatePay pentru a realiza
altceva decit in clasa de baza.
class SalariedEmployee : Employee
{
new public void CalculatePay()
{
Console.WriteLine("SalariedEmployee.CalculatePay()");
}
}

class TestNewMethods
{
static void Main(string[] args)
{
Employee e = new Employee();
e.CalculatePay();

SalariedEmployee s = new SalariedEmployee();
s.CalculatePay();
}
}
Rezultatul este:
Employee.CalculatePay()
SalariedEmployee.CalculatePay()
Ce se intimpla daca omitem cuvintul new?
//new public void CalculatePay()
public void CalculatePay()
{
Console.WriteLine("SalariedEmployee.CalculatePay()");
}
Acest cod se compileaza si rezultatul este identic. In acest caz este implicit.
Cuvintul cheie new este cerut pentru metoda 'NewMethods.SalariedEmployee.CalculatePay()'
pentru ca acesta ascunde membrul mostenit 'NewMethods.Employee.CalculatePay()'.
24
Cele doua metode din clasa de baza si din cea derivata nu sunt in nici un fel de relatie. Faptul
ca au aceeasi semnatura este o simpla coincidenta.
Polymorphism
Metodele suprascrise cu new lucreaza bine daca avem o referinta la un obiect derivat.
Ce se intimpla daca avem o referinta in sus in ierarhie (upcast reference) la o clasa de baza
si dorim ca compilatorul sa apeleze metoda din clasa derivata?
Aceasta problema este rezolvata de polimorfism, ce permite sa definim o metoda de mai
multe ori in cadrul ierarhiei de clase astfel incit la runtime sa se apeleze versiunea corecta
pentru obiectul specificat.
Presupunem ca vrem sa populam un vector cu obiecte din clasa de baza si din clasa derivata.
Dar cum un vector contine elemente de acelasi tip, vom fi obligati sa folosim obiecte din clasa
de baza. Cind vom itera elementele acestui vector dorim sa fie apelata metoda corecta pentru
fiecare obiect, chiar daca obiectul este din clasa derivata si este salvat ca upcast la clasa de
baza, in cadrul acestui vector.
Consideram exemplul urmator in care am mai adaugat o clasa, ContractEmployee. Clasa
aplicatiei principale (contine Main) un tablou cu elemente de tipul Employee si inca doua
metode: LoadEmployees incarca un obiect Employee in vector, si DoPayroll ce itereaza
elementele vectorului, apelind pentru fiecare metoda CalculatePay.
class Employee
{
public string name;

public Employee(string name)
{
this.name = name;
}

public void CalculatePay()
{
Console.WriteLine(
"Employee.CalculatePay called for {0}",
name);
}
}

class ContractEmployee : Employee
{
public ContractEmployee(string name) : base(name)
{
}

public new void CalculatePay()
{
Console.WriteLine(
"ContractEmployee.CalculatePay called for {0}",
name);
}
}

25
class SalariedEmployee : Employee
{
public SalariedEmployee (string name) : base(name)
{
}

public new void CalculatePay()
{
Console.WriteLine(
"SalariedEmployee.CalculatePay called for {0}",
name);
}
}

class TestNotPolymorphic
{
protected Employee[] employees;

public void LoadEmployees()
{
// Simulating loading from a database.
employees = new Employee[2];
employees[0] = new ContractEmployee(
"Adam Barr");
employees[1] = new SalariedEmployee(
"Max Benson");
}

public void DoPayroll()
{
for (int i = 0; i < employees.GetLength(0); i++)
{
employees[i].CalculatePay();
}
}

static void Main(string[] args)
{
TestNotPolymorphic t = new TestNotPolymorphic();
t.LoadEmployees();
t.DoPayroll();
}
}
Rezultatul este:
Employee.CalculatePay called for Adam Barr
Employee.CalculatePay called for Max Benson
Nu e ceea ce am vrut. In fiecare caz s-a apelat metoda din clasa de baza. In fapt s-a folosit
early binding legarea timpurie, realizata de compilator.
Noi avem nevoie de adresa corecta a functiei in timpul executiei, altfel spus late binding.
Pentru a realiza acest lucru vom folosi cuvintele cheie virtual si override pentru metoda in
cauza.
virtual se foloseste in clasa de baza.
override se foloseste in clasa derivata.
26
Exemplu:
class Employee
{
public string name;

public Employee(string name)
{
this.name = name;
}

public virtual void CalculatePay()
{
Console.WriteLine(
"Employee.CalculatePay called for {0}",
name);
}
}

class ContractEmployee : Employee
{
public ContractEmployee(string name) : base(name)
{
}

public override void CalculatePay()
{
Console.WriteLine(
"ContractEmployee.CalculatePay called for {0}",
name);
}
}

class SalariedEmployee : Employee
{
public SalariedEmployee (string name) : base(name)
{
}

public override void CalculatePay()
{
Console.WriteLine(
"SalariedEmployee.CalculatePay called for {0}",
name);
}
}

class TestPolymorphic
{
protected Employee[] employees;

public void LoadEmployees()
{
// Simulating loading from a database.
employees = new Employee[2];
employees[0] = new ContractEmployee(
"Adam Barr");
employees[1] = new SalariedEmployee(
"Max Benson");
}

27
public void DoPayroll()
{
for (int i = 0; i < employees.GetLength(0); i++)
{
employees[i].CalculatePay();
}
}

static void Main(string[] args)
{
TestPolymorphic t = new TestPolymorphic();
t.LoadEmployees();
t.DoPayroll();
}
}
Rezultatul este:
ContractEmployee.CalculatePay called for Adam Barr
SalariedEmployee.CalculatePay called for Max Benson
Functia suprascrisa trebuie sa aiba acelasi nivel de acces (private, protected, public, samd) ca
si functia virtuala pe care o suprascrie.
Un membru virtual nu poate fi declarat privat.

Metode new si virtual
Putem combina new si virtual cand declaram o functie. Pentru ca este new ea ascunde orice
functie mostenita cu aceeasi semnatura. Pentru ca este virtuala ea poate fi suprascrisa in
clasele derivate.
Exemplu

class Employee
{
public string name;

public Employee(string name)
{
this.name = name;
}

public virtual void CalculatePay()
{
Console.WriteLine(
"Employee.CalculatePay called for {0}",
name);
}
}

class SalariedEmployee : Employee
{
public SalariedEmployee (string name) : base(name)
{
}

28
public new virtual void CalculatePay()
{
Console.WriteLine(
"SalariedEmployee.CalculatePay called for {0}",
name);
}
}

class ContractEmployee : SalariedEmployee
{
public ContractEmployee(string name) : base(name)
{
}

public override void CalculatePay()
{
Console.WriteLine(
"ContractEmployee.CalculatePay called for {0}",
name);
}
}

class TestNewVirtuals
{
protected Employee[] employees;

public void LoadEmployees()
{
employees = new Employee[2];
employees[0] = new ContractEmployee(
"Adam Barr");
employees[1] = new SalariedEmployee(
"Max Benson");
}

public void DoPayroll()
{
for (int i = 0; i < employees.GetLength(0); i++)
{
employees[i].CalculatePay();
}
}

static void Main(string[] args)
{
TestNewVirtuals t = new TestNewVirtuals();
t.LoadEmployees();
t.DoPayroll();
}
}
Rezultatul va fi:
Employee.CalculatePay called for Adam Barr
Employee.CalculatePay called for Max Benson

Urmatoareledouadeclaratiisuntidentice:

public new virtual void CalculatePay()
//public virtual void CalculatePay()
29
Metode statice

Metoda statica nu este specifica fiecarei instante a clasei.
Nu e nevoie ca sa existe obiectul pentru a fi apelata.
Exemplu:

class SQLServerDb
{
// Bunch of other nonsalient members
public static void RepairDatabase()
{
Console.WriteLine("repairing database...");
}
}

class StaticMethod1App
{
static void Main(string[] args)
{
SQLServerDb.RepairDatabase();
}
}
Definirea unei metode ca fiind statica se face cu ajutorul cuvantului cheie static.
Sintaxa de apel este:
Class.Method(..);
Urmatorul exemplu este eronat:
static void Main(string[] args)
{
SQLServerDb.RepairDatabase();
SQLServerDb db = new SQLServerDb();
db.RepairDatabase();
}

Accesul la membrii clasei

Ce membrii din cadrul clasei putem accesa din cadrul unei metode statice?
Pot fi accesati membrii statici, nu cei de instanta.
Exemplu:

class SQLServerDb
{
static string progressString1 = "starting repair...";
string progressString2 = "...repair finished";

public static void RepairWithStrings()
{
Console.WriteLine(progressString1); // OK
// Console.WriteLine(progressString2); // Illegal
}
}

class Test
30
{
static void Main(string[] args)
{
SQLServerDb.RepairWithStrings();
}
}
O metoda nestatica poate accesa membri statici cit membri ai instantei.
class SQLServerDb
{
static string progressString1 = "starting repair...";
string progressString2 = "...repair finished";

public void InstanceRepair()
{
Console.WriteLine(progressString1); // OK
Console.WriteLine(progressString2); // OK
}
}

class Test
{
static void Main(string[] args)
{
SQLServerDb db = new SQLServerDb();
db.InstanceRepair();
}
}
Cuvintul cheie this se foloseste numai cu membrii nonstatici.
public SQLServerDb(string s1, string s2)
{
// this.progressString1 = s1; // Illegal
this.progressString2 = s2;
}

Constructori statici
Un constructor static este manipulat intr-un mod special: putem avea un singur ctor static si
fara parametri si normal nu poate apela membri ai instantei, inclusiv pointreul this.
Un ctor static este executat inaintea primei instante ce se vrea creata pentru acea clasa.
Modificatorii de acces nu sunt permisi pentru ctor static.

class SomeClass
{
public static int x;
public int y;

// static public SomeClass() // Access modifiers illegal
// static SomeClass(int i, int j) // Parameters illegal
static SomeClass()
{
x = 1;
// this.y = 2; // this illegal in static method
Console.WriteLine("SomeClass initialized");
31
}
}

class Test
{
static void Main(string[] args)
{
SomeClass sc = new SomeClass();
}
}
Iesirea este:
SomeClass initialized
contrar regulilor de supraincarcare, putem furniza un ctor nonstatic cu aceeasi semnatura ca si
un ctor static. Ctor static va fi apelat primul.
public SomeClass()
{
Console.WriteLine("Non-static ctor");
}
Rezultatul va fi:
SomeClass initialized
Non-static ctor
Ctor static este executat inaintea oricarui membru static, fie data sau functie.
class SomeClass
{
public static int x;

static SomeClass()
{
x = 1;
Console.WriteLine("SomeClass initialized");
}

public SomeClass()
{
Console.WriteLine("Non-static ctor");
}

public static void Foo()
{
Console.WriteLine("Foo");
}
}

class Test
{
static void Main(string[] args)
{
// Any of these 3 lines will invoke the static ctor
SomeClass sc = new SomeClass();
SomeClass.Foo();
32
Console.WriteLine(SomeClass.x);
}
}
33
Proprietati
Proprietatile nu permit accesul direct la membri. Sunt furnizati doi accesori (set, get) ce
lucreaza direct cu data membru. Scopul este de a pastra date cit mai corecte in obiectul
instantiat.
Definirea si folosirea proprietatilor
O propritate in C# consta din declararea unui camp si a unui accesor folosit pentru a-i
modifica valoarea. Accesorii sunt referiti ca metode setter si getter.
[attributes] [modifers] <type> <property-name>{
[ set { <accessor-body> } ]
[ get { <accessor-body >} ]
}
Observatii
Nu e nevoie sa definim ammbii accesori.
get face proprietatea read
set face proprietatea write
proprietatea nu poate fi utilizata ca parametru intr-o metoda.
modificatorul static poate fi utilizat pentru proprietate.

// Example Address class using C# properties
class Address
{
protected string city;
protected string zipCode; // data membru

public string ZipCode // proprietate
{
get { return zipCode; }
set
{
// Validate value against some data store.
zipCode = value; // value cuvant rezervat
// Update city based on validated zipCode.
}
}
}

In client

Address addr = new Address();
addr.ZipCode = "30338";
string zip = addr.ZipCode;

Mostenirea proprietatilor
Modificatorii de acces trebuiesc specificati la nivel de proprietate.
Suprascrierea proprietatilor mostenite
34
Trebuie specificata proprietatea ca fiind virtual in clasa de baza pentru a o suprascrie, iar in
clasa derivata override..
using System;

class CSVFile
{
public CSVFile(string fileName)
{
this.fileName = fileName;
}

protected string fileName;
public virtual string FileName
{
get { return fileName; }
}
}

class CustomerTable : CSVFile
{
public const string FILENAME = "customers.txt";

public CustomerTable() : base(FILENAME) {}

public override string FileName
{
get { return "Customers table"; }
}
}

class OverrideProperties
{
public static void Main()
{
Console.WriteLine("\nInstantiating a CSV object for " +
"the Customer file...");
CSVFile customerFile =
new CSVFile(CustomerTable.FILENAME);
Console.WriteLine("Customer file name = {0}",
customerFile.FileName);

Console.WriteLine("\nInstantiating a Customer " +
"Table object...");
CustomerTable customerTable = new CustomerTable();
Console.WriteLine("Customer file name = {0}",
customerTable.FileName);

Console.ReadLine();
}
}
Intr-o clasa abstracta o proprietate abstracta arata ca in exemplul de mai jos:
abstract class AbstractBaseClass
{
public abstract double AbstractProperty
{
get;
set;
35
}
}
In exemplul urmator este descrisa suprascrierea proprietatilor mostenite.
using System;
using System.Collections;

abstract class Employee
{
protected Employee(int employeeId, int hoursWorked)
{
this.employeeId = employeeId;
HoursWorked = hoursWorked;
}

protected int employeeId;
public int EmployeeId
{
get { return employeeId; }
}

protected int HoursWorked;

protected double hourlyCost = -1; // dummy init value
public abstract double HourlyCost
{
get;
}
}

class ContractEmployee : Employee
{
public ContractEmployee(int employeeId, double hourlyWage,
int hoursWorked)
: base(employeeId, hoursWorked)
{
HourlyWage = hourlyWage;
}

protected double HourlyWage;
public override double HourlyCost
{
get
{ return HourlyWage; }
}
}

class SalariedEmployee : Employee
{
public SalariedEmployee(int employeeId, double salary,
int hoursWorked)
: base(employeeId, hoursWorked)
{
Salary = salary;
}

protected double Salary;
public override double HourlyCost
{
36
get
{
return (Salary / 52) / HoursWorked;
}
}
}

class OverrideProperties
{
public static ArrayList employees = new ArrayList();

public static void PrintEmployeesHourlyCostToCompany()
{
foreach (Employee employee in employees)
{
Console.WriteLine("{0} employee (id={1}) costs {2}" +
" per hour",
employee,
employee.EmployeeId,
employee.HourlyCost);
}
}

public static void Main()
{
ContractEmployee c = new ContractEmployee(1, 50, 40);
employees.Add(c);

SalariedEmployee s =
new SalariedEmployee(2, 100000, 65);
employees.Add(s);

PrintEmployeesHourlyCostToCompany();

Console.ReadLine();
}
}

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