Conceptul de generic este unul foarte puternic in limbajul C#. O introducere in aceasta caracteristica a C# 2.0 am facut in acest articol, despre List.
Spatiul de nume System.Collection.Generic contine interfete care isi au corespondente non-generice in spatiul de nume System.Collections. Acestea sunt: ICollection<T>, IComparer<T>, IDictionary<K,V>, IEnumerable<T>, IEnumerator<T>, IEqualityComparer(T), IList<T>.
In ceea ce priveste clasele din cele doua spatii de nume, o corespondenta ar fi:
Generic | NonGeneric |
Collection<T> | CollectionBase |
Comparer<T> | Comparer |
Dictionary<K,V> | Hashtable |
List<T> | ArrayList |
SortedDictionary<K,V> | SortedList |
Stack<T> | Stack |
Queue<T> | Queue |
ReadOnlyCollection<T> | ReadOnlyCollectionBase |
Reamintim ca despre clasa Dictionary<K,V> am scris in acest articol.
Stack <T>
Colectia de tip Stack (Stiva) este o structura de date de tip LIFO ce stocheaza elemente care pot fi apelate sau sterse printr-un singur pas. Personal, un bun exemplu de a-mi imagina o colectie de tip stiva sunt vasele de la bucatarie care trebuie spalate. :)
Clasa Stack foloseste metoda Push pentru adaugarea unui element si metoda Pop pentru scoaterea unui element din colectie.
Construim o clasa simpla Persoana si sa adaugam obiecte de acest tip intr-o stiva.
class Persoana { public string nume; public string prenume; public Persoana(string Nume, string Prenume) { this.nume = Nume; this.prenume = Prenume; } }
Declaram o colectie de tip stiva, care va stoca elemente de tipul Persoana.
Stack<Persoana> stiva = new Stack<Persoana>();
Apelam functia Push pentru a introduce elemente in stiva :
stiva.Push(new Persoana("Ionescu","Ion")); stiva.Push(new Persoana("Georgescu","George")); stiva.Push(new Persoana("Popescu", "Paul"));
Daca vom afisa elementele stivei, vom observa ca primul element afisat este ultimul element inserat in colectie. (Popescu, Georgescu, Ionescu).
foreach (var p in stiva) { Console.WriteLine(p.nume); }
Dorim sa eliminam un element din stiva. Apelam metoda Pop.
stiva.Pop();
Putem apela proprietatea Count pentru a verifica faptul ca stiva acum va detine cu un element mai putin decat inaintea apelarii metodei Pop. Afisam elementele stivei ca in modul de mai sus si observam ca elementul eliminat prin apelul metodei Pop a fost cel introdus ultimul in colectie. In exemplul nostru, Popescu.
Exista o metoda care va returna obiectul aflat “deasupra”, ultimul introdus in stiva, metoda Peek.
Console.WriteLine(stiva.Peek().nume);
Pentru lista completa a membrilor clasei Stack, site Microsoft.
Queue<T>
Clasa Queue (C0ada) este o structura de date de tip FIFO. Adica, primul element intrat in colectie este primul element care va iesi din colectie. Un exemplu din lumea reala este “coada”, multimea, ce se formeaza la un ghiseu la banca.
Continuam cu acceasi clasa Persoana si vom inseara obiecte de acest tip intr-o colectie Queue.
Declaram colectia:
Queue<Persoana> coada = new Queue<Persoana>();
Clasa Queue detine metoda Enqueue pentru inserarea unui element in colectie si metoda Dequeue pentru eliminarea primului element inserat in lista.
Adaugam cateva elemente:
coada.Enqueue(new Persoana("Ionescu", "Ion")); coada.Enqueue(new Persoana("Georgescu", "George")); coada.Enqueue(new Persoana("Popescu", "Paul"));
Afisam:
foreach (var p in coada) { Console.WriteLine(p.nume); }
Elementele sunt afisate in ordinea in care au fost introduse, primul element inserat fiind si primul afisat.
Dorim sa eliminam un element din colectie si vom apela metoda Dequeue.
coada.Dequeue();
Putem apela proprietatea Count pentru a verifica numarul de elemente al colectiei, care acum va fi mai mic cu un element decat inaintea apelarii metodei Dequeue. Afisam elementele cozii ca in modul de mai sus si observam ca elementul eliminat prin apelul functiei Dequeue a fost cel introdus primul in colectie. In exemplul nostru, Ionescu.
Asemanator clasei Stack, clasa Queue are metoda Peek, care va returna in acest caz, primul element din colectie (“de la inceputul cozii”).
Console.WriteLine(coada.Peek());
Pentru lista completa a membrilor clasei Queue, siteul Microsoft.
Ambele clase contine metoda Clear pentru eliminarea tuturor elementelor din colectie.
coada.Clear(); stiva.Clear();
C# este un limbaj type-safe. Acest lucru presupune sa se stie tipul obiectului inainte de a-i atribui o valoare.
Amintim ca, atunci cand vrem sa folosim una din colectiile generice, trebuie sa declaram astfel:
using System.Collections.Generic;
.Net are un suport foarte vast pentru colectii. Exista foarte multe clase, metode, proprietati, pentru a interactiona cu structuri de date variate din framework.
Ca avantaje ale colectiilor strong type: folosirea indecsilor, enumerarea, redimensionarea dinamica.
Pentru ca sunt foarte puternice, se recomanda folosirea cu atentie a colectiilor si alegerea corecta a tipului de colectie necesar.
Mai multe informatii despre spatiul de nume System.Collections.Generic exista pe msdn.
Am lasat la urma, poate si din cauza ca e cea mai folosita, List<T>. Este alternativa la ArrayList pe care o folosesc de cand am descoperit clasele generice. Aceasta clasa implementeaza o serie de interfete:
[SerializableAttribute] public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
care permit accesul la diverse facilitati despre care voi discuta mai detaliat intr-un articol viitor.
La ce e buna folosirea acestei clase? Din punctul meu de vedere ea este un ArrayList imbunatatit, in primul rand prin type safe. List<T> este pentru mine o colectie care stie sa se redimensioneze automat pe masura ce ii adaog noi elemente si care “stie” ce tip de elemente i-au fost introduse (<T>). Nu o sa vin in cazul acestei clase cu exemple de cod, pentru ca am dat anterior, sau in alte articole de pe acest blog, destule exemple, ci doar o sa enumar cateva din situatiile practice in care poate fi folositoare.
Exemple:
1. Avem o lista de obiecte, sa zicem persoane, si trebuie sa pastram intr-o lista numai persoanele cu varsta de peste 18 ani. Nu stim de la inceput cate persoane au peste 30 de ani. Alternativele sunt: ori numaream rezultatele (intr-o variabila cont), tracand o data peste lista numai pentru a numara, si creem un array de persoane de dimensiunea cont insa aceasta abordare presupune parcurgerea de doua ori a listei, o data pentur numarare si inca o data pentru adaugarea persoanelor cu varsta de peste 18 ani in array; alta varianta este folosirea unui ArrayList, care se redimensioneaza dinamic pe masura ce sunt adaugate personae, insa care are dezavantajul ca la fiecare accesare a unui obiect al listei, obiectul accesat trebuie convertit (cast) in Person; a treia varianta pe care eu o vad este tocmai varianta care foloseste o lista generica, List<Person>, care realizeaza o lista care se mareste dinamic si care permite accesul direct, fara cast, la obiectele stocate in ea.
2. Citim un fisier text, si trebuie sa pastram intr-un tablou toate liniile de text care contin text, altceva decat spatii goale. Nu stim cate linii vom gasi, asa ca cea mai potrivita abordare e crearea unui obiect de tip List<string> unde sa pastram liniile de text gasite.
3. Avem un set de date dintr-un tabel dintr-o baza de date, sa zicem sub forma unui DataTabel, si trebuie sa pastram numai anumite date (randuri), care indeplinesc anumite criterii, eventual sub forma unor obiecte construite din datele de pe randul respectiv.
4. Citim un fisier XML unde avem definita limba folosita de aplicatia noastra. Numarul de cuvinte definite este necunoscut, asa ca o lista dinamica de string este solutia cea mai potrivita.
5. Citim un fisier HTML si vrem sa extragem toate linkurile continute. Din fiecare link construim un obiect de tip Uri pe care le pastram intr-o lista de tip List<Uri>.
In urmatorul articol, vom descrie System.Diagnostics, un spatiu de nume care contine o serie de clase foarte interesante.