Limbajul c sharp



Yüklə 1,48 Mb.
səhifə10/14
tarix08.01.2019
ölçüsü1,48 Mb.
#92362
1   ...   6   7   8   9   10   11   12   13   14

Pentru a dezvolta exercitiul de mai sus,sirurile preluate pot fi

separate cu metoda SPLIT,pentru a obtine subsiruri ce pot fi procesate

independent.De exemplu,cantitatea si pretul pot fi transformate in valori

numerice,pentru a le putea utiliza in operatii matematice.In acest mod,

o banala fila de tip text poate fi utilizata la fel ca si un tabel de

date.Chiar daca nivelul de securitate este foarte redus (datele sunt

foarte usor de corupt),acest mecanism simplu poate fi o alternativa pentru

datele de tip "open source".

Daca se utilizeaza List in loc de ArrayList,exista si o metoda ce

permite conversia automata a datelor.

EXEMPLU:

using System;

using System.Drawing;

using System.Collections.Generic;

public class Exemplu {

public static void Main() {

List
lista1 = new List
();

lista1.Add(new PointF(15.7F,33.22F));

lista1.Add(new PointF(77.13F,15.3F));

lista1.Add)new PointF(1401.3F,8.1F));

Console.WriteLine();

foreach( PointF p in lista1)

{ Console.WriteLine(p); }

List
lp = lista1.ConvertAll(

new Converter
(Transformare));

Console.WriteLine("Dupa conversie:");

foreach( Point p in lp)

{ Console.WriteLine(p); }

Console.ReadLine();

}

public static Point Transformare(PointF pf)



{ return new Point(((int) pf.x),((int) pf.y)); }

}

Salvati fila cu numele Lista2.cs si compilati.



In exemplul de mai sus,o lista de puncte definite prin valori de tip

float,este convertita la o lista de puncte definite prin valori de tip

int.Se utilizeaza metoda standard ConvertAll().

Acest exemplu,ilustreaza destul de clar avantajul claselor generice

fata de cele tipizate.Aceeasi clasa de obiecte (List) arhiveaza

initial elemente de tip PointF,apoi elemente de tip Point.

Pentru a putea efectua operatii mai complexe de sortare si filtrare

a datelor,au fost dezvoltate si clase de tip colectie in care fiecare

element este caracterizat printr-o pereche de variabile: una pentru cheia

de indexare si cea de a doua pentru valoarea propriu zisa.Acest gen de

obiecte seamana foarte mult cu tabelele organizate pe linii si coloane

si pot executa cu succes orice operatie de preluare si prelucrare a

datelor dintr-o baza de data (formeaza tamponul intermediar dintre baza

de date si aplicatie).

Exista numeroase exemple de astfel de clase,dar cele mai simple sunt

HashTable si corespondentul sau de tip generic:

Dictionary

-75-


EXEMPLU:

using System;

using System.Collections.Generic;

public class Exemplu{

public static void Main() {

Dictionary dic1 =

new Dictionary();

dic1.Add("txt","notepad.exe");

dic1.Add("bmp","paint.exe");

dic1.Add("dib","paint.exe");

dic1.Add("rtf","wordpad.exe");

Console.WriteLine();

foreach( KeyValuePair p1 in dic1)

{ Console.WriteLine("Extensie= {0},Program= {1}",p1.Key,p1.Value); }

Console.ReadLine();

}}

Salvati fila cu numele Dictionar1.cs si compilati.



Fata de obiectele de tip List,aceste obiecte permit si o serie

de bucle de filtrare conditionate prin cheia de indexare.Aceasta cheie

de indexare,poate fi orice cod alfanumeric.

EXEMPLU:


in exercitiul de mai sus,se pot filtra toate elementele cu valoarea

"paint.exe",pentru a obtine extensia filelor produse,sau se pot filtra

toate elementele cu extensia >"mdb".Practic se lucreaza la fel ca si

in cazul tabelelor,unde elementele pot fi selectate fie in functie de

coloana din tabel,fie in functie de linia din tabel.

Daca necesitatile de programare o impun si nici una dintre clasele

standard nu satisface aceste necesitati,se pot declara si defini clase

noi de tip colectie,cu campuri de date,proprietati si metode specifice.

Nu este obligatoriu,dar este recomandabil ca si aceste clase nou definite

sa respecte sablonul general al claselor tip colectie,adica sa contina:

un enumerator,o metoda de clonare a datelor (CopyTo),capacitatea de a

numara elementele din container (Count),si una sua mai multe metode de

sincronizare a thread-urilor paralele.

Exista si clase de tip colectie specializate pentru un anumit tip

de date.

EXEMPLU: StringDictionary implementeaza o clasa de tip Hash Table in

care atat cheia de indexare cat si valoarea elementelor este strict de

tip string.

Alte clase de tip colectie se adapteaza la contextul de executie.

EXEMPLU: HybridDictionary class implementeaza o clasa ListDictionary

atat timp cat numarul de elemente arhivate este mic,dar se transforma

automat in Hash Table imediat ce numarul de elemente depaseste indexul

numeric( cel din domeniul de definitie initial ),practic schimba automat

metoda de indexare a elementelor.

Mai mult decat atat,o serie de obiecte vizuale,utilizeaza automat

obiecte de tip colectie,pentru a executa o serie oarecare de operatii.

EXEMPLU: Un obiect de tip ComboBox,va apela la un obiect de tip List

sau Dictionary pentru a-si incarca automat lista de optiuni,citind

datele dintr-o resursa oarecare (utilizeaza un link spre resursa).

Incercati sa editati o lista cu aplicatiile claselor de tip colectie.


-76-


EXCEPTII SI ERORI
O exceptie este o situatie neasteptata,ce intervine in timpul executiei

unui program.O astfel de exceptie poate fi genrata de un cod imperfect,de

incompatibilitati ale datelor preluate din diverse resurse,de sistemul de

operare,de o eroare umana a utilizatorului sau de coruperea accidentala a

datelor.

EXEMPLU: -aplicatia citeste date dintr-o fila de resurse si aceasta fila

este stearsa sau suprascrisa accidental de un alt utilizator.

Nu se pot scrie coduri absolut perfecte,dar o mare parte dintre aceste

accidente de executie pot fi preintampinate.Pentru acest scop,platforma

.Net include o clasa specializata,denumita EXCEPTION.In momentul in care

executia programului este intrerupta de o exceptie,sistemul de operare va

genera automat un obiect de tip EXCEPTION,in care va arhiva pe scurt,tipul

de situatie depistata.In mod normal,executia va fi intrerupta,iar eroarea

va putea fi identificata doar de catre un programator avizat.

De cele mai multe ori,exceptia care a generat intreruperea programului

nu este vitala pentru executia aplicatiei.Din acest motiv,a fost imaginat

un mecanism prin care programul sa poata identifica si corecta eroarea,

pentru a putea continua executia,chiar daca o parte dintre date vor fi

afectate de eroare.Prin acest mecanism,se permite proceselor automate sa

elibereze memoria,sau sa comunice cu alte aplicatii paralele,inainte de a

intrerupe programul pentru remedierea defectiunii.Acest mecanism este

foarte important,mai ales atunci cand mai multe aplicatii conlucreaza in

retea pentru un anumit scop.

Cele mai frecvente erori generate de sistem sunt:

System.ArithmeticException - este generata de o eroare aritmetica

System.ArrayTypeMismatchException - incompatibilitatea tipului de data

System.DivideByZeroException - apare la impartirea prin zero

System.IndexOutOfRangeException - apare la un index negativ sau inexistent

System.InvalidCastException - este generata la erorile de implementare

System.NullReferenceException - apare la apelul unor date nedefinite

System.OutOfMemoryException - apare la supraincarcarea memoriei

System.OverFlowException - apare cand se depaseste domeniul de definitie

System.StackOverFlowException - tamponul de executie este supraincarcat

System.TypeInitializationException - constructorul static emite exceptia


In toate aceste situatii de executie,sistemul de operare identifica

eroarea si construieste automat un obiect de tip Exception,in care descrie

situatia identificata si momentul in care a intervenit.Pentru ca executia

sa nu fie intrerupta,toate situatiile in care este posibil sa apara astfel

de exceptii,pot fi incluse intr-o bucla de tip TRY...CATCH,sau pot fi

conditionate prin atribute,sau prin directive de precompilare.Atributele

au fost prezentate intr-un capitol anterior,asa ca in acest capitol se

vor exemplifica doar buclele TRY...CATCH,desi se pot imagina si alte

solutii pentru tratarea exceptiilor.

Spre deosebire ce C++ si Java,buclele TRY...CATCH din limbajul C sharp,

permit si o bucla suplimentara denumita FINALLY,ce se executa in ambele

situatii: daca intervine sau daca nu intervine o exceptie.Bucla FINALLY

se utilizeaza de obicei pentru operatiile de finalizare a executiei si

eliberare a memoriei (dar se poate exploata si pentru alte scopuri).

-77-

Cel mai simplu exemplu este o eroare aritmetica generata de impartirea



prin zero.

EXEMPLU:


using System;

class Exceptie {

public static void Main() {

F1 f1 = new F1();

Console.Write(" 12/4 = ")'

int x = f1.Fractie(12,4);

Console.WriteLine(x);

Console.Write(" 12/0= ");

x= f1.Fractie(12,0);

Console.WriteLine(x);

Console.ReadLine();

}}

class F1 {



public int Fractie(int x,int y){

try { return (x / y); }

catch (System.DivideByZeroException dbz)

{ System.Console.WriteLine(dbz); return 0; }

}}

Salvati fila de mai sus cu numele Exceptie1.cs si compilati.Pentru a



observa diferenta,rescrieti exercitiul fara bucla TRY...CATCH.

In exemplul de mai sus,daca nu intervine exceptia se executa bucla TRY,

iar daca intervine exceptia se executa bucla CATCH.Acelasi mecanism se

putea obtine si cu un atribut conditional (vezi capitolul).

Exceptiile nu sunt intotdeauna identificate doar de sistemul de operare.

Daca este necesar,exceptiile si mesajele de eroare pot fi elibarate de

orice proces de executie cu o comanda THROW.Dupa eliberare,aceste exceptii

pot fi identificate si tratate la fel ca si cele eliberate de sistem.

EXEMPLU:

using System;

public class Exceptie2 {

static void Main() {

try {

Console.WriteLine("Se executa bucla TRY ! ");



throw new NullReferenceException();

}

catch (NullReferenceException e)



{ Console.WriteLine("EXCEPTIE= {0} ",e); }

finally


{ Console.WriteLine("Se executa bucla FINALLY !"); }

Console.ReadLine();

}}

Salvati fila cu numele Exceptie2.cs si compilati.



In exemplul de mai sus,se observa ca exceptia a fost emisa chiar din

interiorul buclei TRY...CATCH si a fost interceptata si tratata imediat.

In mod normal,o astfel de exceptie este emisa de un proces din alt modul,

sau chiar de un alt utilizator din retea,pentru a semnala o anumita

situatie,ce necesita o functie de tratare a exceptiei.

Exemplu: un anumit utilizator va fi exceptat de la acces la program.

-78-

Probabil ca exceptia cea mai frecevnta din sistem este generata de



absenta uneia dintre resursele apelate.De exemplu,un set oarecare de date

esentiale trebuie sa fie citite dintr-o fila de resurse,inainte de a fi

procesate.

EXEMPLU:


using System;

using System.IO;

public class ProcessFile{

public static void Main() {

FileStream fs = null;

try {


fs = new FileStream("Filadorita.txt",FileMode.Open);

StreamReader sr = new StreamReader(fs);

string line;

line = sr.ReadLine();

Console.WriteLine(line);

}

catch(FileNotFoundException e)



{

Console.WriteLine("[Fila cautata nu exista !] {0}",e);

}

finally


{

if (fs != null) fs.Close();

}

Console.ReadLine();



}}

Salvati fila cu numele Exceptie3.cs si compilati.

In exemplul de mai sus,se incerca deschiderea si citirea unei file

care nu exista in memorie.Bucla CATCH va intercepta eroarea de tip

FileNotFoundException si va executa blocul de tratare a exceptiei.Daca

adaugati o fila denumita Filadorita.txt,in care editati un text oarecare,

se va executa bucla TRY,apoi se va executa automat si bucla FINALLY,prin

care se inchide stream-ul si se elibereaza memoria.Daca fila ramane

deschisa,nu va mai putea fi apelata pana la inchiderea programului.Daca

fila respectiva este o resursa din retea,ea va ramane blocata pentru toti

ceilalti utilizatori,pana la terminarea programului.Din acest motiv,este

esential ca atunci cand se citesc date din file de resurse,sa se adauge

si o bulca automata pentru inchiderea filei.

Emiterea,interceptarea si tratarea erorilor se poate face si pentru

situatii exceptionale,in care doriti sa implementati un anumit artificiu

de programare,sau doriti sa subliniati un anumit aspect.Totusi nu este

recomandabil sa se abuzeze de acest mecanism.Fiecare exceptie si bucla de

interceptare a exceptiei,fragmenteaza executia,introduce cel putin inca

un thread suplimentar,adauga seturi intregi de operatii si intarzie

executia programului.Din acest motiv,este recomandabil ca situatiile ce

pot genera exceptii sa fie evitate atunci cand este posibil si sa fie

tratate doar atunci cand nu pot fi evitate prin alt procedeu.Exista si

programatori analisti,care construiesc un sistem complex de depanare

automata a programelor,dar cu pretul unui consum nejustificat de memorie

suplimentara.Decizia finala apartine fiecarui programator.

-79-


THREADS (Fire de executie)
Evolutia tehnicii de calcul a dus la dezvoltarea unor procesoare din

ce in ce mai performante,capabile sa lucreze simultan cu volume din ce

in ce mai mari de informatie.Totusi,memoria de executie a procesorului

este limitata,mult mai mica decat volumul total al programelor prelucrate.

Din acest motiv,sistemul de operare imparte datele ce intra in procesor

in calupuri mai mici,ce urmeaza sa fie procesate succesiv.Fiecare astfel

de bloc de date,nu este impartit dezordonat,ci este inclus intr-un stream,

si este organizat cu ajutorul unui obiect specializat.Aceste obiecte

poarta numele de thread = fir de executie.Aceste fire de executie permit

o serie de operatii:

1. -mai multe aplicatii pot fi executate simultan

2. -se poate stabili o lista de prioritati,astfel incat aplicatiile sa

fie executate preferential atunci cand memoria este limitata

3. -se pot sincroniza doua sau mai multe aplicatii,pentru a organiza

competitia fata de aceleasi file de resurse

4. -se pot executa in background operatii de intretinere,supraveghere si

control,managementul memoriei,depanare automata...etc.

Sistemul de operare Windows,exemplifica cel mai clar acest gen de

operatii.Astfel,utilizatorul poate sa scrie un text,sa asculte muzica si

simultan sa copieze niste date din reteaua Internet.Fiecare aplicatie

este controlata de sistemul Windows cu ajutorul unui thread separat,

incarcat intr-un stream cu executie indepenedenta.Ca rezultat,oricare

dintre aplicatii va putea fi intrerupta sau lansata,in orice moment.

Procesarea paralela poate fi importanta si in cazul aplicatiilor C#.

Presupunand ca aplicatia este formata din mai multe module executabile

mici,acestea vor putea fi lansate in executie cu un AppDomain.

EXEMPLU:

using System;

class Thread1 {

static void Main() {

AppDomain dom1 = AppDomain.CreateDomain("d1");

dom1.ExecuteAssembley(@"c:\v3\Clasa1.exe");

dom1.ExecuteAssembley(@"c:\v3\Clasa3.exe");

AppDomain.Unload(dom1);

}};

Salvati fila cu numele Thread1.cs si compilati.



In exemplul,de mai sus,exista un singur thread de executie (cel creat

automat de sistemul de operare),iar procesarea se face serial.Ca rezultat,

cele doua ferestre nu pot fi deschise simultan.Dupa ce se inchide prima

fereastra se va deschide cea de a doua.In cazul in care cele doua

ferestre trebuie sa fie deschise simultan,solutia este ca fiecare dintre

ele sa fie lansata in executie cu un thread separat.

Platforma .Net,ofera clasa specializata Thread din System.Threading,

cu ajutorul careia se pot construi obiecte de tip thread.Fiecare obiect

va contine metode si proprietati ce permit organizarea fluxului de

executie.

In exemplul de mai sus,se poate crea cate un thread pentru fiecare

dintre cele doua ferestre.In acest mod,fiecare dintre cele doua module

va putea fi executat si controlat separat.

-80-


EXEMPLU:

using System;

using System.Threading;
class Thread1 {

public class Worker {

public void Fir1(){

AppDomain dom1 = AppDomain.CreateDomain("d1");

dom1.ExecuteAssembley(@"c:\v3\Clasa1.exe");

AppDomain.Unload(dom1);

}

public void Fir2(){



AppDomain dom2 = AppDomain.CreateDomain("d2");

dom2.ExecuteAssembley(@"c:\v3\Clasa3.exe");

AppDomain.Unload(dom2);

}}

static void Main(){



Worker fir1 = new Worker();

Thread t1 = new Thread(fir1.Fir1);

t1.Start();

Worker fir2 = new Worker();

Thread t2 = new Thread(fir2.Fir2);

t2.Start();

}}

Salvati fila cu numele Thread2.cs si compilati.



In exemplul de mai sus,Clasa1.exe si Clasa3.exe sunt doua module

executabile create anterior (vezi capitolul clase).Se observa ca pentru

a crea un thread,constructorul are ca parametru o functie,adica functia

ce urmeaza sa fie executata in acel stream.Pentru ca modulele sa fie

complet independente,am inclus in functia fiecaruia dintre ele toate

operatiile necesare,inclusiv cele de eliberare a memoriei.Ca rezultat,

cele doua ferestre lansate in executie,se comporta ca si cand ar fi

doua programe complet independente.Lansarea in executie a unui thread

se face prin metoda Start(),iar intarzierea executiei se poate face prin

metoda Sleep().

In exemplul de mai sus,ambele thread-uri sunt declarate identic,asa

ca sunt procesate in ordinea in care apar in program.In mediul real de

executie Windows,procesorul lucreaza simultan cu cateva mii de astfel

de thread-uri (vezi fereastra Windows Task Manager).Atunci cand memoria

de operare este la limita,sistemul de operare va elimina o parte dintre

aceste thread-uri de la executie si le va transfera intr-un tampon de

memorie auxiliara,unde stau "pe linie de asteptare",pana cand pot fi

reluate spre executie.Acest proces se executa in functie de ordinea in

care au fost trimise spre procesor si in functie de dimensiunea fiecarui

thread.Pentru a controla acest proces,se pot seta prioritati de executie

pentru thread-urile esentiale,cu ajutorul proprietatii Thread.Priority.

Exista cinci grade de prioritate: Highest,AboveNormal,Normal,BelowNormal,

si Lowest.Pentru a simplifica executia,thread-urile care contin operatii

esentiale,cum ar fi destructorii si eliberarea memoriei,vor fi setate cu

prioritatea cea mai mare (Highest),iar cele care contin doar operatii

optionale sau neimportante,vor fi setate cu prioritatea cea mai mica.

-81-

O alta proprietate extrem de utila este Thread.ThreadState.Daca se



citeste aceasta proprietate in diferite momente ale executiei,se poate

afla status-ul de moment al firului de executie.Aceste volori pot fi:

Aborted,AbortRequested,Background,Running,Stopped,StopRequested,Suspended,

SuspendRequested,Unstarted sau WaitSleepJoin.Cunoasterea status-ului

pentru fiecare thread este esentiala in etapa de depanare.

EXEMPLU:


using System;

using System.Threading;


public class ThreadExemplu {

public static void Proces1() {

for(int i = 0; i < 3; i++){

Console.WriteLine("Proces1- operatia: {0} ",i);

}}

public static void Proces2() {



for(int i = 0; i < 3; i++){

Console.WriteLine("Process2- operatia: {0} ",i);

}}

public static void Proces3() {



for(int i = 0; i < 3; i++){

Console.WriteLine("Process3- operatia: {0} ",i);

}}

public static void Main(){



Console.WriteLine("Incepe thread-ul Main: ");

Thread t1 = new Thread(new ThreadStart(Proces1));

t1.Priority = ThreadPriority.Lowest;

t1.Start();

Console.WriteLine("Firul1 este= {0}",t1.ThreadState);

Thread t2 = new Thread(new ThreadStart(Proces2));

t2.Priority = ThreadPriority.Highest;

t2.Start();

Console.WriteLine("Firul1 este= {0}",t1.ThreadState);

Console.WriteLine("Firul2 este= {0}",t2.ThreadState);

Thread t3 = new Thread(new ThreadStart(Proces3));

t3.Priority = ThreadPriority.Normal;

t3.Start();

Console.WriteLine("Firul1 este= {0} ",t1.ThreadState);

Console.WriteLine("Firul3 este= {0} ",t3.ThreadState);

t1.Join();t2.Join();t3.Join();

Console.WriteLine("Firul1 este= {0} ",t1.ThreadState);

Console.ReadLine();

}}

Salvati exemplul cu numele Thread3.cs si compilati.



In exemplul de mai sus,sunt trei fire de executie cu prioritati

diferite.Se observa cum firul t1,cel cu prioritatea cea mai mica este

stopat pana cand se executa cele doua fire cu prioritate mai mare.Pentru

crearea firelor de executie au utilizat clasa delegat ThreadStart prin

care se conecteaza thread-ul la metoda ce urmeqza sa fie executata (vezi

System.Threading).Metoda Join(),blocheaza fiecare fir,pana la terminarea

executei.

-82-


O situatie particulara apare atunci cand mai multe fire de executie,

exploateaza acelasi obiect (sau fila de resurse).Daca un fir de executie

intrerupe operatiile executate de alt fir de executie,pentru a castiga

accesul la resurse,atunci primul fir de executie va fi invalid,sau chiar

mai mult decat atat,va contine date invalide.Pentru a evita acest gen de

situatii,este necesara sincronizarea firelor de executie,astfel incat la

un anumit moment dat,un singur thread sa poata accesa obiectul sursa.

Cea mai simpla sincronizare se face cu ajutorul unui obiect de tip

Monitor,care prin metoda Enter() blocheaza resursa si prin metoda Exit()

elibereaza resursa.

EXEMPLU:

using System;

using System.Threading;

using System.Collections;

public class T1 {

public static Queue stiva1 = new Queue();

public static void Proces1() {

for (int i = 0; i < 10; i++) {

Monitor.Enter(stiva1);

stiva1.Enqueue("text");

Console.WriteLine("Proces1 - operatia: {0}", i);

Monitor.Exit(stiva1);

Thread.Sleep(250);

}}

public static void Proces2() {



for (int i = 0; i < 10; i++) {

Monitor.Enter(stiva1);

stiva1.Enqueue("text");

Console.WriteLine("Proces2- operatia: {0}", i);

Monitor.Exit(stiva1);

Thread.Sleep(150);


Yüklə 1,48 Mb.

Dostları ilə paylaş:
1   ...   6   7   8   9   10   11   12   13   14




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin