de acces (constrangerile claselor generice).Cu alte cuvinte,acest tip de
solutie,permite impartirea clientilor in grupe cu nivelele diferite de
acces la informatie,fara a fi necesara introducerea de parole,coduri de
acces,sisteme de securizare...etc.
Clasele generice reprezinta forma cea mai evoluata de structurare a
datelor,in cadrul conceptului general de programare orientata spre obiect.
Atentie insa: genericele deschid o poarta larga spre nenumarate erori de
executie,exceptii de format,cazuri particulare,incompatibilitati,bucle
infinite,tipuri nedefinite,erori de sintaxa,conversii gresite...etc.
-49-
ENUMERARI
Enumerarile sunt un tip special de data,definit de catre utilizator.
Este asemanator cu o arie sau cu o lista,in sensul ca poate arhiva si
ordona grupe de date.Fiecare element poate primi o denumire,astfel incat
sa fie cat mai usor de identificat.Enumerarile au rostul de a memora un
set oarecare de valori constante,ce urmeaza sa fie apoi utilizate in
program,pentru setari sau operatii default.Spre deosebire de variabilele
simple,enumerarile garanteaza faptul ca valoarea aleasa fece parte din
setul de valori acceptabile,arhivat anterior.
Biblioteca System,contine o clasa enum,astfel ca declararea unui astfel
de obiect se face folosind cuvantul enum,urmat de lista de elemente cu
virgula intre ele.Fiecare element poate fi o constanta,din orice tip
Integer cu exceptia tipului Char.Daca nu se specifica tipul de data,se
va utiliza implicit tipul Int32.Asadar,o enumerare este un spatiu denumit
ce include o lista de elemente de tip integer.
Clasa enum mosteneste clasa ValueType si implementeaza interfetele
IComparable,IFormattable si IConvertible (vezi referintele),astfel ca
asigura si un set minimal de metode implicite prin care se pot compara
doua instante ale clasei,se pot face conversii de format intre tipul
Integer si tipul String...etc.
EXEMPLU:
using System;
public class Enumerare {
enum Culori { rosu,galben,albastru,verde,alb };
static void Main(){
Console.WriteLine("Primul element este: {0}",Culori.rosu);
Type col = typeof(Culori);
foreach (string s in Enum.GetNames(col))
Console.WriteLine("{0,-11}={1}",s,
Enum.Format(col,Enum.Parse(col,s),"d"));
Console.ReadLine();
}}
Salvati fila cu numele Enumerare1.cs si compilati.
In exemplul de mai sus,se declara o enumerare,apoi se utilizeaza o
bucla foreach,pentru a citi toate elementele din lista,dar si pentru a
face conversia valorilor numerice atribuit automat.Pentru a atribui
alte valori,se utilizeaza operatorul de atribuire "=".
EXEMPLU: enum Culori{ rosu=22,galben=33,albastru,verde=55,alb };
de remarcat este faptul ca in declaratia de mai sus,culoarea albastru
va primi automat valoarea 33 (cea a elementului precedent + 1) ,iar
culoarea alb va primi automat valoarea 56).Pentru verificare,puteti sa
repetati exercitiul cu declaratia de mai sus.
Enumerarile se utilizeaza frecvent,fie pentru a preseta un set de
variabile,fie pentru a verifica daca o valoare oarecare face parte din
setul de valori acceptabile.De exemplu,pentru a crea un filtru de valori,
se declara o enumerare,apoi se apeleaza rutina prin care se verifica daca
valoarea respectiva face parte din enumerare.
Daca se utilizeaza o bucla de tip SWITCH ... CASE este posibil ca
fiecare element din enumerare sa declanseze o anumita functie de raspuns.
-50-
EXEMPLU:
using System;
public class Enumerare {
enum Culori { rosu,galben,albastru,verde,alb };
static void Main(){
Culori col = Culori.rosu | Culori.verde;
switch(col) {
case Culori.verde:
Console.WriteLine("Culoarea verde este in lista!");
break;
}
Console.ReadLine();
}}
Salvati fila cu numele Enumerare2.cs si compilati.
In exemplul de mai sus,bucla switch...case nu numai ca verifica daca
elementul cautat face parte din lista,dar permite si executia unei rutine
de raspuns la apel.Intr-un program mai mare,utilizatorii vor avea cate un
set de valori prestabilite,in functie de optiunile pentru care au optat.
In momentul executiei,fiecare utilizator va putea avea o configuratie
proprie,in functie de unul sau mai multe astfel de seturi de valori
prestabilite.
Daca elementele din enumerare sunt de alt tip decat Int32,acest tip
trebuie specificat explicit,atat atunci cand se declara enumerarea cat si
atunci cand se citeste un element al sau.
EXEMPLU:
using System;
public class Enumerare {
enum Domeniu : long { Max = 123456789L,Min = 4321L };
static void Main() {
long x = (long)Domeniu.Max;
long y = (long)Domeniu.Min;
Console.WriteLine("Valoarea maxima este = {0},x);
Console.WriteLine("Valoarea minima = {0}",y);
Console.ReadLine();
}}
Salvati exemplul cu numele Enumerare3.cs si compilati.
Enumerarile sunt esentiale nu numai ca tip de data in sine,dar si
pentru intelegerea unor structuri de date complexe,cum sunt iteratorii,
sau pentru implementarea unor interfete cum sunt:IEnumerator,IEnumerable.
Tipul enum,este important mai ales pentru a garanta siguranta in
executie a sistemului.Din acest motiv,este esential ca toate operatiile
cu si asupra elementelor din enumerari sa se faca prin rutine automate.
Daca utilizatorul seteaza manual datele din enumerari,sau poate interveni
direct asupra lor,printr-un mecanism oarecare,atunci aceastea nu vor mai
prezenta nici un fel de avantaj fata de o arie de date sau o variabila
simpla.Alegeti acest tip de data,atunci cand doriti sa implementati un
set de rutine automate,independente de interactiunea cu utilizatorul.
Aceasta recomandare este valabila si atunci cand enumerarile sunt
incluse la randul lor in structuri mai complexe (vezi si iteratorii),
sau in procese de setare si actualizare a unei aplicatii.
-51-
ITERATORI
Dupa cum le spune si numele,iteratorii sunt structuri de date destinate
unor operatii repetitive (iterative).Iteratorii nu sunt o inventie a
limbajului C#,ci exista sub o forma sau alta in toate limbajele de
programare.Buclele FOR...NEXT,FOREACH,DO...WHILE sunt exemplele cele mai
simple de operatii iterative.Totusi,in conceptul general de programare
orientata spre obiect,termenul de iterator se utilizeaza doar pentru
obiectelele sau functiile destinate sa parcurga elementele unei colectii,
sau sa permita navigarea prin selectie,sortarea,filtrarea sau selectarea
elementelelor de un anumit tip.Cu alte cuvinte,iterartorii sunt o solutie
software pentru automatizarea unor procese.
In particular,in limbajul C# aceste operatii sunt simplificate prin
existenta unui set de interfete: IEnumerable si IEnumerable,respectiv
IEnumerator si IEnumerator.Aceste interfete definesc un set intreg de
metode standard usor de implementat.Se utilizeaza pentru a selecta si
returna grupuri de elemente dintr-o colectie.Functia prin care se pot
returna datele dorite,prezinta urmatoarele caracteristici:
1.-elementul returnat trebuie sa fie unul din cele patru tipuri mentionate
mai sus: IEnumerable,IEnumerator,IEnumerable sau IEnumerator
2.-pentru a desemna datele returnate se utilizeaza cuvantul "yield" urmat
de cuvantul cheie "return" (yield = a produce,a returna)
3.-fiecare iterator trebuie a fie denumit distinct,la fel ca o clasa.O
clasa poate contine mai multi iteratori.Daca o colectie necesita mai
multi iteratori alternativi,in momentul compilarii se va crea o clasa
speciala pentru fiecare iterator,pentru a permite un mai bun control
al memoriei consumate (se impacheteaza datele).
Cea mai simpla operatie o reprezinta parcurgerea succesiva a datelor
dintr-o colectie.
EXEMPLU:
using System;
using System.Collections;
class ListClass : System.Collections.IEnumerable {
public System.Collections.IEnumerator GetEnumerator()
{ for (int i = 0, i < 10; i++) { yield return i; }
}}
class ProgramulMeu{
static void Main(){
ListClass listClass1 = new ListClass();
foreach( int i in listClass1)
{ System.Console.WriteLine("Valoarea returnata este: "+i);}
Console.ReadLine();
}}
Salvati fila cu numele Iterator1.cs si compilati.
In exemplul de mai sus,clasa ListClass implementeaza o interfata de
tip IEnumerable si supraincarca metoda GetEnumerator() cu o metoda noua
personalizata,in care se genereaza si se returneaza 10 elemente de tip
int.Obiectul listClass1,generat din aceasta clasa este un iterator ce
permite parcurgerea membrilor sai cu ajutorul unei bucle foreach.
Un iterator este foarte usor de recunoscut,de la prima vedere,dupa
cuvantul cheie yield return.
-52-
Nu se justifica construirea unui iterator,doar pentru a parcurge toate
elementele dintr-o colectie.Acelasi rezultat se poate obtine cu o bucla
simpla,FOREACH sau FOR...NEXT.In mod normal,iteratorul contine o functie
specializata,prin care se executa o serie oarecare de operatii asupra
datelor returnate,fara a modifica datele din resursa exploatata.Fie ca
este vorba de operatii aritmetice sau de formatare,fie ca se executa
selectii,sortari sau filtrari,iteratorul trebuie sa automatizeze un set
oarecare de operatii.
EXEMPLU:
using System;
using System.Collections;
class ListClass : System.Collections.IEnumerable {
string[] nume = { "Ion","Dan","Stefan","Constantin"};
public System.Collections.IEnumerator GetEnumerator()
{ for (int i=0; i< nume.Length; i++) { yield return nume[i]+"escu,";}
}}
class ProgramulMeu{
static void main(){
ListClass listClass1 = new ListClass();
Console.WriteLine("Numele dun lista sunt: ");
Console.WriteLine("");
foreach (string i in listClass1)
{ System.Console.Write(i); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator2.cs si compilati.In exemplul de mai
sus,iteratorul nu numai ca parcurge intreaga colectie,dar executa si o
operatie oarecare (adauga sufixul "escu").In mod similar,se pot executa
serii de calcule aritmetice (Exemplu: calculul impozitelor etc.).Pentru
fiecare set de operatii,se poate defini un astfel de iterator personalizat
ce poate fi arhivat in memorie sub forma de biblioteca DLL.Cu o astfel
de solutie,se pot valorifica datele dintr-o resursa,dupa algoritmi
personalizati.Spre deosebire de un tabel sau o baza de date,o astfel de
solutie prezinta o siguranta crescuta in exploatare,deoarece datele nu
pot fi valorificate decat in prezenta iteratorului.
Mai mult decat atat,interfata IEnumerable expune un enumerator de
tip generic si un set complet de metode,ce permit operatii similare cu
sistemul de interogari SQL utilizat in cazul bazelor de date.Dintre
metodele acestei interfete,merita amintite:Aggregate,All
Any,Average,Contains,Count,Distinct<
TSource>,Elements,First,GroupBy,GroupJoin<
TSource>,Join,Last,Max,Min,OrderBy<
TSource>,Select,Single,Sum,ToList,
Union,Where,Zip (vezi referinta MSDN).
Exista mai multe metode supraincarcate,pentru fiecare dintre operatiile
de mai sus,astfel ca aceasta interfata se poate adapta practic la orice
necesitati de programare curenta.Interfata poate fi apelata direct,caz
in care nu este necesar sa redefiniti sau sa supraincarcati nici una
dintre metode,ci puteti apela direct metoda standard.Cambinand iteratorii
cu selectii de tip LINQ (vezi si capitolul urmator),se obtine o foarte
mare flexibilitate de filtrare a datelor de orice tip.
-53-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu{
class Angajat
{ public string Nume { get; set; }
public int Vechime { get; set; }
}
static void Main() {
Angajat[] an1 = {
new Angajat { Nume="Ion Ionescu",Vechime=18 },
new Angajat { Nume="Mihai Popescu",Vechime=14},
new Angajat { Nume="Stefan Georgescu",Vechime=3 },
new Angajat { Nume="Tudor Pop",Vechime=2 }
};
IEnumerable query = an1.OrderBy(angajat => angajat.Vechime);
foreach (Angajat a in query)
{ Console.WriteLine("Nume= {0} vechime= {1} ani",
a.Nume,a.Vechime); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator3.cs si compilati.
In acest exemplu se creaza colectia an1 direct din tipul IEnumerable
si apoi se apeleaza metoda OrderBy() pentru a obtine sortarea elementelor
in functie de Vechime.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu {
class Angajat { public string Nume { get; set; }
public bool Casatorit { get; set; } }
static void Main() { Angajat[] an1= {
new Angajat { Nume = "Maria Anton",Casatorit=false},
new Angajat { Nume = "Ion Ionescu",Casatorit=true },
new Angajat { Nume = "Mihai Popescu",Casatorit=true },
new Angajat { Nume = "Stefan Georgescu",Casatorit=true },
new Angajat { Nume = "Tudor Pop",Casatorit=false}
};
int nr1 = an1.Count( p => p.Casatorit == false);
int nr2 = an1.Count( p => p.Casatorit == true);
Console.WriteLine("{0} angajati sunt necasatoriti.",nr1);
Console.WriteLine("{0} angajati sunt casatoriti.",nr2);
Console.ReadLine();
}}
Salvati fila cu numele Iterator4.cs si compilati.
In acest caz se apeleaza metoda Count(Func,Boolean)
pentru a separa grupuri de elemente,in functie de un criteriu boolean.Se
observa ca interfata IEnumarable este apelata implicit (prin Count()).
-54-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu {
class Copil { public string Nume { get; set; } }
class Angajat { public string Nume { get; set; }
public Copil[] c1 { get; set; } }
static void Main(){
List an1 = new List {
new Angajat {
Nume = "Ion Ionescu", c1 = new Copil[]{ new Copil{ nume="Ion"}}},
new Angajat {
Nume = Mihai Popescu",c1 = new Copil[]{} },
new Angajat {
Nume = "Ana Pop",c1= new Copil[]{ new Copil{ Nume="Maria" },
new Copil{ Nume="Mihai" }}}
};
IEnumerable nr = from Angajat in an1 where Angajat.c1.Any()
select Angajat.Nume;
foreach (string a in nr)
{ Console.WriteLine("Angajatul {0} are copii.",a); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator5.cs si compilati.
Observati ca in acest caz,s-a utilizat o selectie LINQ,asemanatoare
cu o comanda de tip SQL.
Numarul de astfel de solutii,este practic infinit.Mecanismul de iterare
a unei colectii de date cu un astfel de iterator prezinta o serie intreaga
de avantaje:
1.-toate datele sunt incluse in obiecte,fiind astfel usor de manevrat in
memoria de operare,usor de gestionat si usor de eliberat din memorie
2.-metodele standard sunt simple,intuitive,usor de aplicat si pot fi
apelate direct (fara sa fie redefinite sau supraincarcate )
3.-iteratorii nu modifica resursa,nu adauga variabile suplimentare,nu
interfereaza cu alte date din sistem si utilizeaza tampoane de memorie
proprii,fara pointeri sau link-uri auxiliare
4.-datele sunt destul de bine securizate,deoarece un programator neavizat
nu poate exploata resursa,decat daca are acces la fila DLL si cunoaste
codul iteratorului.
Bineinteles ca executabilele pot fi deschise si studiate cu un utilitar
de genul ildasm.exe,dar este necesar ca programatorul sa detina notiuni
avansate de programare pentru a descifra codul,caz in care este foarte
putin probabil sa modifice negativ functionalitatea programului.
Interfetele necesare sunt arhivate in System.Collections.Generic si
respectiv in System.Linq.Are rost sa studiati intreaga lista de metode,
pentru fiecare interfata.Puteti sa formulati combinatii personalizate,sau
puteti sa preluati si transformati formule standard,prezentate in alte
exemple.Pentru formularea selectiilor de tip LINQ vezi si capitolul
urmator.
-55-
SELECTII LINQ
LINQ (Language-Integrated Query) este denumirea prescurtata pentru un
set de tehnologii software ce permit selectarea unor subseturi sau seturi
de date,din structuri de date de tip colectie.In general,o selectie este
o expresie prin care se solicita o preluare de informatii,dintr-o sursa de
date.Acest gen de solutie a fost dezvlotat mai ales pentru prelucrarea de
date arhivate in tabele si in baze de date (date preformatate).Fiecare
sistem de baze de date,a fost conceput cu un anumit format,astfel ca si
selectiile de date au trebuit sa respecte anumite criterii fixe.Cel mai
cunoscut limbaj de acest gen,este SQL (Structured Query Line).LINQ nu
este decat o extensie a limbajului SQL,creata cu scopul de a permite o
flexibilitate crescuta in preluarea de date,din surse formatate diferit.
Astfel,prin selectii LINQ se pot prelua si prelucra date arhivate in
memoria de operare,in tampoane temporare,in arii de date sau in liste,
in file de tip text,XML,in tabele si baze de date,sau in orice alt tip
de suport de memorie.
Tehnologia LINQ implica trei etape diferite:
1.Constructia sursei de date,se poate face astfel:
-se arhiveaza date pe un suport fix
-se preiau date din una sau mai multe surse din retea
-se construiesc date complet noi in memoria de operare
-se convertesc datele existente in mediul de executie
2.Formularea expresiei de interogare (selectie)
3.Executia expresiei LINQ si procesarea datelor preluate din sursa.
Cel mai simplu exemplu,preia datele din memoria de executie.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable selectie =
from numar in lista where numar < 80 select numar;
Console.WriteLine("Numerele mai mici decat 80 sunt:");
foreach (int i in selectie)
{ Console.Write(i = " "); }
Console.ReadLine();
}}
Salvati exemplul cu numele Linq1.cs si compilati.
O expresie de tip selectie LINQ se formuleaza la fel ca si o expresie
de tip SQL,cu ajutorul unor clauze,ce pot fi declarate intr-o anumita
ordine prestabilita.Astfel,o expresie LINQ trebuie sa inceapa cu clauza
"from",urmata de resursa in care sunt arhivate datele si trebuie sa se
termine cu clauza "select" prin care se pot transforma datele preluate
intr-o secventa noua de date,reformatate sau reformulate.Acest tip de
transformare mai poarta si numele de "proiectie" (projection).Intre cele
doua clauze obligatorii,pot exista una sau mai multe clauze optionale,cum
sunt: where,orderby sau join,prin care se specifica tipul de operatii
necesare pentru a transforma datele preluate.
-56-
De exemplu,in exercitiul anterior clauza select prelucreaza datele
din lista si preia doar pe cele care respecta clauza where.Pentru a
sorta datele si in ordine descendenta,se poate adauga o clauza "orderby".
EXEMPLU:
using System;
using Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable selectie =
from numar in lista
where numar < 80
orderby numar descending
select numar;
Console.WriteLine("Numerele mai mici decat 80 sunt: ");
foreach (int i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq2.cs si compilati.
Se pot combina doua sau mai multe clauze de acelasi tip,sau se pot
combina doua sau mai multe conditii,cu ajutorul operatorilor OR sau AND.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable selectie =
from numar in lista
where numar < 80 && numar > 10
select numar;
Console.WriteLine("Numerele selectate sunt: ");
foreach (int i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq3.cs si compilati.
Daca sursa de date este la randul sau o colectie de date,se pot utiliza
doua sau mai multe clauze "from",pentru a separa fiecare element.
EXEMPLU: from country in countries
from city in country.Cities
where city.Population > 100000
select city;
va selecta toate obiectele de tip city din fiecare colectie country,
arhivata in colectia countries si va extrage doar pe cele care respecta
conditia din where.
Pentru ca expresia sa evalueze si rezultatul unei alte expresii se
poate utiliza clauza "let".
-57-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main() {
string[] lista = { "Popescu","Danescu","Stefanescu" };
var selectie = from nume in lista
let subsir = nume.Split(new char[] {'e'})[0]
select subsir;
Console.WriteLine("Rezultatul obtinut este: ");
foreach (string i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq4.cs si compilati.Observati si faptul ca
tipul de data IEnumerable poate fi inlocuit cu succes prin tipul "var".
Pentru a asocia sau combina date preluate din doua surse diferite,se
poate utiliza clauza "join".Selectia se va face cu ajutorul unei operatii
de comparare intre elementele specificate din fiecare sursa de date.
In LINQ,operatii de tip "join" se pot efectua si pe secvente de date ce
contin tipuri diferite de data.Dupa operatia "join" trebuie neaparat sa
urmeze o clauza select sau groupby,prin care sa se specifice elementele
ce urmeaza sa fie preluate din secventa obtinuta prin reuniune.Tipul var,
denumit si tip anonim,poate fi utilizat atunci cand prin operatia de
reuniune doriti sa obtineti un tip de data nou,diferit de cele initiale.
EXEMPLU:
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
Dostları ilə paylaş: |