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
Dostları ilə paylaş: |