Limbajul c sharp



Yüklə 1,48 Mb.
səhifə6/14
tarix08.01.2019
ölçüsü1,48 Mb.
#92362
1   2   3   4   5   6   7   8   9   ...   14

ajutorul claselor de tip Delegate.Clasa Delegate,definita in System si

varianta sa mai perfectionata MulticastDelegate sunt niste clase speciale

ce nu pot fi utilizate pentru a crea obiecte.Declararea acestor clase se

face prin cuvantul cheie "delegate",in loc de "class",tocmai pentru a se

evita orice fel de confuzii.

Daca se declara o astfel de clasa,compilatorul va crea automat o clasa

delegat (la care programatorul nu are acces).Aceasta clasa va avea niste

metode intrinseci (BeginInvoke,Invoke si EndInvoke) prin care asigura

functionalitatea automata (nu pot fi apelate din program).Clasa astfel

creata va contine si o conexiune spre metoda dorita,astfel ca prin apelul

delegatului sa poata fi pusa in executie metoda dorita.Pentru a realiza

o astfel de conexiune sunt necesare urmatoarele etape:

1. se declara delegatul si tipul parametrilor din functia conectata

2. se declara metoda ce urmeaza sa fie conectata la obiect

3. se creaza un obiect de tip delegat

4. se utilizeaza delegatul in locul metodei

EXEMPLU:

using System;

namespace Program1{

delegate void DelegatulMeu();

class Test{

static void Main(){

DelegatulMeu d1 = new DelegatulMeu(FunctiaMea);

d1();


Console.ReadLine();

}

static void FunctiaMea() {



Console.WriteLine("Se executa metoda conectata !"); }

}

}



Salvati exemplul cu numele Delegat1.cs si compilati.

Asadar se declara o clasa de tip delegate,denumita DelegatulMeu,fara

nici un parametru.Ca urmare,acest delegat va putea fi conectat la orice

functie fara nici un parametru.Apoi se declara functia dorita si se

face conexiunea construind un obiect de tip delegat (spre functia dorita).

Acesta este cel mai simplu exemplu posibil,cu o singura functie,fara

nici un parametru.Practic,aproape ca nu are rost crearea unui delegat,

decat pentru a simplifica apelul.

Exista insa si situatii in care se utilizeaza mai multe functii,cu

mai multi parametri,dar o singura clasa delegat.In acest caz,se va

crea automat o clasa de tip MulticlastDelegate (definita tot in System).

Singura conditie obligatorie este ca toate metodele conectate sa contina

aceiasi parametri ca si delegatul.

-41-


EXEMPLU:

using System;

namespace DelegatMultiplu {

public delegate int Delegat1(int x,int y);

public class Mate{

public static int Adun(int x,int y){ return x+y; }

public static int Mul(int x,int y){ return x*y;}

public static int Divide(int x,int y){ return x/y;}

}

public class Executie {



public static void Main(){

Delegat1 a1 = new Delegat1(Mate.Adun);

Delegat1 m1 = new Delegat1(Mate.Mul);

Delegat1 d1 = new Delegat1(Mate.Divide);

Console.WriteLine("Delegatul pentru adunare returneaza:");

Console.WriteLine(a1(32,4));

Console.WriteLine("Delegatul pentru inmultire returneaza:");

Console.WriteLine(m1(32,4));

Console.WriteLine("Delegatul pentru impartire returneaza:");

Console.WriteLine(d1(32,4));

Console.ReadLine();

}}}


Salvati fila cu numele Delegat2.cs si compilati.

Observati ca in acest caz,clasa de tip MulticastDelegate va putea

conecta orice functie cu doi parametri de tip int.Conexiunea cu functia

nu se face decat in momentul in care se creaza obiectul de tip delegat.

Astfel,cu o singura clasa se pot crea delegati spre mai multe functii

diferite.Eventual se poate construi chiar o arie de delegati ce pot fi

apelati serial,cu rutine automate.Acest gen de solutie,simplifica foarte

mult situatiile in care un numar oarecare de functii si metode trebuie

sa fie apelate repetat,dupa un algoritm oarecare.

Daca deschideti modulul Delegat2.exe cu utilitarul ildasm.exe puteti

observa ca namespace DelegatMultiplu contine trei clase: Delegat1,Executie

si Mate.Dintre acestea,clasa Delegat1 este clasa de tip MulticastDelegate

creata automat in momentul compilarii.Daca deschideti aceasta clasa puteti

observa metodele intrinseci.

Cu o singura astfel de clasa,se pot controla zeci sau sute de functii

si metode diferite (inlocuind zeci sau sute de clase si pointeri).Se

observa robustetea cu care este gestionata memoria.Din acest motiv,clasele

de tip "delegate" sunt utilizate in limbajul C# pentru a gestiona toate

evenimentele din program.In locul functiilor pentru captarea,filtrarea

si tratarea mesajelor Windows,se utilizeaza delegati.

Biblioteca System include un set destul de bogat de delegati standard

ce pot fi utilizati pentru problemele curente de programare.Dintre acestia

cei mai importanti sunt: Action,Action,Action...Action

AppDomainInitializer,AsyncCallback,Comparison,Converter

EventHandler,EventHandler,Func,Func,

Predicate,ResolveEventhandler si UnhandledExceptionEventhandler.

Pentru detalii si exemple de implementare,este recomandabil sa studiati

sursele bibliografice pentru .Net si 3.5 Framework Class Library sau

manualele de tip tutorial din retea.

-42-


Cea mai frecventa aplicatie a claselor delegat o reprezinta gestionarea

evenimentelor Windows.Pentru acest scop,se utilizeaza clasele delegate:

EventHandler si EventHandler.In cazul evenimentelor,clasa

de tip delegate conecteaza obiectul sender(cel care emite mesajul Windows)

cu functia pentru tratarea evenimentului.Majoritatea functiilor de tratare

a evenimentului asteapta un singur mesaj Windows,adica au un singur

parametru.Ca urmare,cu o singura clasa de tip delegate se pot gestiona

toate evenimentele unei intefete grafice.

EXEMPLU:

using System;

using System.Drawing;

using System.Windows.Forms;

namespace Eveniment1 {

public class MyForm: System.Windows.Forms.Form {

TextBox t1 = new TextBox();

Button b1 = new Button();

public MyForm(){

t1.Location = new Point(20,30);

b1.Text = "Apasa aici !";

b1.Location = new Point(20,55);

b1.Size = new Size(150,20);

b1.Click += new EventHandler(OnClick);

this.Controls.Add(t1);

this.Controls.Add(b1);

this.Resize += new EventHandler(OnResize);

}

public void OnResize(object sender,EventArgs ee) {



MessageBox.Show("oops! Redimensionare !"); }

public void OnClick(object sender,EventArgs e) {

t1.Text = "Hello user !"; }

[STAThread]

static void Main() { Application.Run(new MyForm());}

}}

Salvati fila cu numele Ev1.cs si compilati.



Observati ca prin operatorul += se executa de fapt doua operatii

simultane,se creeaza un nou obiect de tip EventHandler (de tip delegate)

si se conecteaza obiectul ca metoda de tratare a evenimentului.

Prin acest mecanism,toate evenimentele din interfata grafica pot fi

controlate cu ajutorul obiectelor de tip EventHandler ce inlocuiesc

tabela de pointeri creata in mediul Windows.

Daca deschideti fila Ev1.exe cu utilitarul ildasm.exe,puteti observa

ca nu se creaza o clasa delegat noua ci se utilizeaza System.EventHandler.

Obiectele EventHandler se creaza in momentul executiei si exista doar in

memoria de operare.Fila Ev1.exe nu contine decat clasa Eveniment1.MyForm.

Cele doua obiecte pot fi regasite in metoda constructor (.ctor:void()).

Prin acest mecanism robust si simplu,C# nu numai ca protejeaza aceste

obiecte de la suprascrierea lor accidentala,dar si executa operatiile

esentiale la adapost de privirile indiscrete ale "hacker-ilor".

Evenimentele si clasele delegat,reprezinta un punct nodal in executia

programelor C#.Din acest motiv,este recomandabil sa studiati in amanunt

toata documentatia referitoare la aceste subiecte.

-43-


INDEXATORI
Sunt o inovatie a limbajului C#,cu scopul de a permite transformarea

unei clase,intr-un container indexat la fel ca o arie.Datele vor putea

fi introduse si extrase la fel ca intr-o stiva si vor putea fi sortate

in functie de criteriul de indexare.Nu este obligatoriu ca indexarea sa

se faca prin valori numerice de tip INT.Utilizatorul poate alege orice

alta formula de indexare doreste.

Un indexator (indexer) este un membru al clasei,asemanator cu o

proprietate.La fel ca si proprietatile,indexatorii se declara cu cate o

metoda set() si get() prin care se executa de fapt introducerea sau

extragerea datelor din stiva.

EXEMPLU:

using System;

class Container

{

private T[] arie = new T[100];



public T this[int i]{

get { return arie[i]; }

set { arie[i] = value; }

}}

class ProgramulMeu



{

static void Main(string[] args)

{

Container text1 = new Container();



text1[0] = "text1";

text1[5] = "text5";

text1[7] = "text7";

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

System.Console.WriteLine(text1[i]); }

Console.ReadLine();

}}

Salvati fila cu numele Indexator1.cs si compilati.



Observati ca pentru declararea indexatorului nu se utilizeaza un cuvant

cheie special,ci se utilizeaza "this",adica pointerul spre obiect.Cu alte

cuvinte,indexatorii nu sunt decat un mic artificiu de programare,mai exact

o metoda de personalizare a clasei respective.

Indexatorii pot fi supraincarcati,sau pot avea mai mult decat un singur

parametru.In exemplul de mai sus,clasa contine un singur parametru de tip

generic ,astfel incat in momentul crearii unui obiect sa poata fi

atribuit orice tip de data.Exemplu: pentru a lucra cu date de tip INT,

se poate construi un obiect astfel :

Container int1 = new Container();

Indexatorii sunt o solutie simpla,atunci cand se lucreaza cu un volum

relativ mic de date,pentru care programatorul doreste o solutie speciala

de indexare.Pentru volume mari de date,exista structuri de date special

concepute,cum sunt iteratorii,listele,listele generice,enumeratorii,

tabelele si bazele de date...etc.

Are rost sa declarati un indexator,doar atunci cand doriti sa efectuati

cateva operatii simple,dupa un algoritm personalizat.

-44-


GENERICE
Genericele nu au fost inventate de limbajul C#.Stilul de programare in

care se utilizeaza date de tip generic,a fost introdus in anul 1983 de

limbajul Ada.Genericele sunt o forma de abstractizare a limbajului prin

care se permite declararea unui set de operatii (algoritm) abstracte,la

care nu se cunoaste tipul de date asupra caruia se opereaza.Operatiile

devin reale,doar in momentul crearii unei instante,in care se declara

explicit si tipul de date.Acest mecanism permite o flexibilitate foarte

mare de programare,mai ales in cazul limbajelor puternic tipizate,cum

sunt C++ si C#.Prin acest mecanism,se poate declara o functie,ce are un

parametru virtual,declarat conventional prin litera T.Acest paramatru

virtual,va putea fi inlocuit in momentul crearii unei instante,cu orice

tip de data.Pentru a semnaliza faptul ca este vorba despre un tip generic

se utilizeaza parantezele ascutite < si > in locul celor rotunde.Se pot

utiliza,unul sau mai multi astfel de parametrii generici,pentru a putea

formula o functie cat mai laxa,ce se poate adapta,in functie de contextul

de memorie din momentul executiei.

EXEMPLU:

using System;

public class Container{

public void Scrie(T input){

Console.WriteLine("S-a adaugat elementul: {0}",input);

}}

class ProgramulMeu{



static void Main(){

Container text1 = new Container();

text1.Scrie("Textul meu");

Container text2 = new Container();

text2.Scrie(33);

Console.ReadLine();

}}

Salvati fila cu numele Generic1.cs si compilati.



Observati ca pentru clasa Container s-a declarat parametrul generic

T.Acest generic a fost apoi utilizat in metoda Scrie().Se poate spune

despre clasa Container ca este o clasa generica.In momentul crearii

unei instante,se va putea preciza tipul de data,pentru a putea executa

un anumit set de operatii.Se observa ca s-au creat doua instante diferite

cu doua tipuri de data diferita,utilizand aceeasi clasa generica.

Pentru a obtine acelasi rezultat cu clase obisnuite,ar fi trebuit sa

fie declarata cate o clasa separata,pentru fiecare tip de data.In plus,

in momentul crearii instantei,ar fi trebuit sa fie incarcata in memorie

si clasa sablon.In cazul claselor generice,se declara si se incarca in

memorie o singura clasa,din care se pot crea apoi oricate instante,

indiferent cu ce tip de data.Prin acest mecanism,se face si o economie

importanta de memorie,atunci cand numarul de instante este foarte mare.

Un generic in care tipul de data este virtual (Exemplu: Container)

se numeste generic "deschis",deoarece poate accepta orice tip de data,

in timp ce un generic in care se precizeaza tipul de data prin inlocuirea

tipului virtual cu unul real (Exemplu: Container) se numeste

generic "inchis",deoarece nu poate accepta decat acel tip de data.

-45-

In mod curent,genericele deschise (open generic) se utilizeaza atunci



cand se declara clasa,iar genericele inchise (closed generic) atunci cand

se creaza instanta (obiectul).

In exemplul precedent,clasa Container defineste o metoda unica,fara

a face distinctia intre un tip de data sau altul.In mod curent insa,se

pot utiliza formule de filtrare,prin care rezultatul functiei este

diferit,in functie de tipul de data al instantei.

EXEMPLU:

using System;


public class Container{

public void Scrie(T input){

if(typeof(T) == typeof(string)){

Console.WriteLine("S-a adaugat textul: {0}",input);}

if(typeof(T) == typeof(int)){

Console.WriteLine("S-a adaugat numarul INT: {0}",input);}

}}

class ProgramulMeu{



static void Main(){

Container text1 = new Container();

text1.Scrie("Textul meu");

Container text2 = new Container();

text2.Scrie(33);

Console.ReadLine();

}}

Salvati fila cu numele Generic2.cs si executati.



Se observa ca in acest caz,functia Scrie() se comporta diferit daca

instanta este de tip String sau de tip Int.Prin acest mecanism se pot

programa clase si metode "universale",ce pot fi apelate cu orice tip

de data (dintre cele definite in clasa generica),sau se pot programa

"filtre" de date.Clasele generice se pot utiliza cu succes si pentru

tratarea exceptiilor,sau pentru depanarea automata.

Cea mai importanta trasatura a genericelor este capacitatea de a se

modela,in functie de contextul de memorie.Probabil ca aplicatia cea mai

valoroasa o reprezinta capacitatea de a realiza compatibilitatea dintre

doua tabele,sau doua baze de date diferite.Astfel daca doua tabele contin

coloane cu tipuri de date diferite,se poate crea un "filtru",cu ajutorul

unei clase generice,ce va permite conversia automata a datelor si

implicit copierea de date,dintr-un tabel in altul.In mod similar,un astfel

de filtru poate conecta intre ele,arii de date,liste,enumerari si clase

container,sau orice alt tip de structuri de date.Cel mai frecvent,acest

mecanism este util,pentru a putea exploata resurse mai vechi,editate in

alt format sau arhivate in alt tip de container decat cel actual.

Genericele sunt utilizate pe larg si in .Net Framework,fie sub forma

de clase,fie sub forma de interfete,metode,constante si variabile,clase

delegat...etc.Dinte aceste resurse,cele mai frecvent utilizate sunt

clasele generice din biblioteca System.Collections.Generic.Toate aceste

clase sunt specializate pentru un anumit tip de operatii si ofera un

set oarecare de metode standardizate,ce pot fi apelate direct.Clasele

standardizate sunt usor de depanat de catre orice programator sau permit

crearea unor rutine de depanare automata.

-46-


Dintre clasele tipizate,cea mai comuna este clasa List.Aceasta

clasa este un container ce poate organiza liste de obiecte.Intr-un astfel

de container,obiectele din lista pot fi apelate prin indexul lor.List

este un obiect echivalent cu o arie de date,la care se adauga metodele

standard: Contains,IndexOf,LastIndexOf,Remove,BinarySearch sau Sort.

EXEMPLU:


using System;

using System.Collections.Generic;


public class Exemplu{

public static void Main(){

List nume = new List();

Console.WriteLine("\n Capacitate: {0}",nume.Capacity);


nume.Add("Mircea");

nume.Add("Stefan");

nume.Add("Tudor");

nume.Add("Isabela");

nume.Add("Ana");
Console.WriteLine();

foreach(string persoana in nume)

{ Console.WriteLine(persoana); }

Console.WriteLine("\n Capacitate: {0}",nume.Capacity);

Console.WriteLine("Inregistrari: {0}",nume.Count);

Console.WriteLine("\n Contine elementul:(\"Isabela\")? ={0} ",

nume.Contains("Isabela"));

List numere = new List();

numere.Add(3);

numere.Add(27);

numere.Add(38);

foreach(int numar in numere)

{ Console.WriteLine("Elementul este: {0}",numar); }

Console.ReadLine();

}}

Salvati fila cu numele Generic3.cs si compilati.



In exemplul de mai sus,clasa List a fost apelata pentru a crea doua

obiecte de tip lista.Primul denumit "nume" ce contine obiecte de tip

string si al doilea,denumit "numere" ce contine obiecte de tip Int.

Se observa ca obiectele din lista pot fi citite secvential cu o bucla

foreach (sau cu o bucla definita de programator).

In plus,metodele obiectului permit operatii simple de genul:

-cate inregistrati contine lista

-cate inregistrari s-au adaugat la ultima operatie

-ce capacutate are obiectul

-eliminarea unor obiecte din lista

-etc.

Lista generica este un obiect robust,usor de utilizat,ce inlocuieste



cu succes orice structura,enumerare sau container cu indexatori.Este

recomandabil sa utilizati acest tip de obiect,mai ales in modulele pe

care doriti sa le partajati si cu alti programatori.

-47-


O alta clasa generica similara cu List,dar mai perfectionata este

clasa Dictionary.Aceasta clasa contine doi parametri generici

si permite astfel un numar foarte mare de implementari posibile.Poate

asigura toate operatiile executate de List,la care se adauga si un

set intreg de operatii de indexare in functie de TKey.Fiecare obiect de

tip TValue este indexat la un obiect de tip TKey,pentru a forma perechi

de obiecte,ce permit numerosi algoritmi pentru sortarea sau filtrarea

lor.


EXEMPLU:

using System;

using System.Collections.Generic;
public class Example{

public static void Main(){


Dictionary dic1 = new Dictionary();

dic1.Add("Mircea","doctorand");

dic1.Add("Stefan","inginer de sistem");

dic1.Add("Tudor","proiectant de sistem");

dic1.Add("Isabela","studenta");

dic1["Ana"] = "economist";


foreach(KeyValuePair pereche in dic1)

{ Console.WriteLine("Cod = {0}, Valoare = {1}",

pereche.Key,pereche.Value); }
Dictionary dic2 = new Dictionary();

dic2[3] = 33;

dic2[7] = 125;

dic2[11] = 7;

dic2[1] = 22;
foreach( KeyValuePair pereche in dic2)

{ Console.WriteLine("Index = {0}, Valoare = {1}",

pereche.Key,pereche.Value); }

Console.ReadLine();

}}

Salvati fila cu numele Generic4.cs si compilati.



In exemplul de mai sus se creaza instante doar pentru doua variante

dintre toate combinatiile posibile.Trebuie remarcat faptul ca fiecare

dintre parametrii poate fi orice tip de data predefinit,sau orice tip de

data definit de utilizator (inclusiv clase,delegati,structuri,arii etc.).

Ca rezultat,numarul de variante este practic infinit.In mod curent insa,

se utilizeaza pentru TKey fie un cod alfanumeric de tip string,fie un

index numeric de tip Int,iar pentru TValue se utilizeaza orice tip de

data.Tipul Dictionary,se utilizeaza pentru a putea identifica

cat mai usor,o anumita inregistrare,in functie de un anumit cod.De exemplu

pentru a crea un utilitar de tip "Help",in care TKey este cuvantul cautat,

iar TValue va fi textul explicativ.

Containerele de tip generic,pot inlocui cu succes tabelele si bazele

de date,atunci cand se lucreaza doar cu date "run time" (efemere).

-48-


Clasele generice deschise accepta orice tip de data definit in program.

Exista insa si situatii in care doriti sa impuneti un anumit tip de

restrictii,sau de limitari,astfel incat clasa generica sa nu poata accepta

decat o anumita subcategorie de tipuri de data,sau chiar doar un anumit

tip de data.Aceste constrangeri (constrains) se pot obtine adaugand

cuvantul cheie "where" urmat de tipul de constrangere.In cazul in care

clientul incearca sa creeze o instanta cu un alt tip de data,se va returna

o eroare de compilare.Sunt posibile urmatoarele constrangeri:

1. where T: struct -argumentul trebuie sa fie de tip valoric

2. where T: class -argumentul trebuie sa fie o referinta spre o clasa

o interfata,un delegat sau o arie

3. where T: new() -argumentul trebuie sa detina un constructor fara

nici un parametru

4. where T: -argumentul trebuie sa fie clasa de baza

sau o clasa derivata din clasa de baza

5. where T: -argumentul trebuie sa fie,sau sa

implementeze si interfata specificata

6. where T: U -argumentul trebuie sa fie argumentul genericului

U sau sa fie derivat din acesta

EXEMPLU:


using System;
public class s1 {

public s1(){ Console.WriteLine()("Textul dorit"); }

}

public class Container where T: s1 {



public void Scrie(T input){

Console.WriteLine("Este textul preluat din fisier!");

}}

class ProgramulMeu{



static void Main(){

Container text1 = new Container ();

text1.Scrie(new s1());

Console.ReadLine();

}}

Salvati fila cu numele Generic5.cs si compilati.



In exemplul de mai sus,clasa generica Container nu poate fi apelata

decat pentru obiecte de tip s1.Presupunand ca exista o arhiva de memorie

formata din obiecte de tip s1 si clientul lucreaza cu o interfata grafica

in care se solicita datele dorite,acesta va trebui sa introduca in caseta

de solicitare numele clasei s1.Orice alt tip de data va returna o eroare.

Clienti diferiti,vor avea acces la fisiere diferite,in functie de codurile


Yüklə 1,48 Mb.

Dostları ilə paylaş:
1   2   3   4   5   6   7   8   9   ...   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