.Net Framework contine doua seturi de interfete standard pentru enumerarea si compararea colectiilor. Un set nontype-safe, IEnumerable si IEnumerator si un set type-safe, IEnumerable <T> si IEnumerator <T>. Toate intefetele din .Net Framework, prin conventie, isi incep numele cu I.
Interfata IEnumerable contine o singura metoda, GetEnumerator. Obiectul returnat de aceasta metoda, este un enumerator folosit pentru parcurgerea elementelor colectiei, el implementand interfata IEnumerator.
In general, cand se implementeaza interfata IEnumerable, se implementeaza si interfata asociata IEnumerator.
Interfata IEnumerator
Obiectul enumerator este folosit pentru parcurgerea elementelor din colectie (il putem privi ca un indicator care arata catre elementele dintr-o lista).
Interfata Ienumerator are proprietatea:
object Current {get;}
si metodele :
bool MoveNext() – va directiona indicatorul catre urmatorul element din lista. Va returna true, daca mai exista in colectie un alt element, false, daca nu.
void Reset() – va returna indicatorul inapoi la primul element din lista.
Prin construirea unui enumerator, cu ajutorul metodei GetEnumerator a unei colectii, si prin apelarea repetata a metodei MoveNext si preluarea valorii din proprietatea Currentfolosind un enumerator, putem naviga prin elementele unei colectii din element in element. Daca vrem sa construim o colectie de clasa enumerabila, trebuie sa implementam interfata IEnumerable in colectia de clase si sa furnizam implementarea interfetei IEnumerator care sa fie returnata de metoda GetEnumerator a colectiei de clase.
Se observa ca proprietatea Current nu are un comportament type-safe, ea returnand un object. Framework-ul .Net pune la dispozitie interfata generica IEnumerator<T>, a carei proprietate va returna un obiect de tip T. La fel si in cazul interfetei IEnumerable<T>, a carei metoda GetEnumerator va returna un Enumerator<T>. Exista totusi diferente, intre cele doua. Una ar fi ca, interfata IEnumerator<T> nu contine metoda Reset, extinzand, insa, mai mult, interfata IDisposable.
In exemplul urmator, vom folosi interfetele IEnumerable si IEnumerator pentru a folosi un foreach pentru obiecte ale clasei Cont, la fel cum il folosim pentru tipurile de baza.
Construim clasa Cont care va contine 3 variabile membru, numarul unic al contului, numele persoanei care detine contul si soldul contului si vom creea constructorul clasei (articolul despre introducere in programarea pe obiecte). Mai adaugam si functia toString, pe care o vom suprascrie pentru a afisa in consola pentru fiecare obiect al clasei, toti cei trei membri (articolul despre polimorfism).
//variabile membru private int numarCurent; private string numeTitular; private double sold; //constructor public Cont(int numarCurent, string numeTitular, double sold) { this.numarCurent = numarCurent; this.numeTitular = numeTitular; this.sold = sold; } //suprascriem functia //pentru a afisa toate variabilele membru public override string ToString() { return "Nr.Crt.:" + this.numarCurent.ToString() + "\nNume:" + numeTitular + "\nSold:" + sold.ToString(); }
Vom construi clasa Conturi, care va contine un vector de obiecte Cont. Clasa va contine si o metoda de adaugare in lista a obiectelor si va implementa cele doua interfete (articolul despre interfete).
//clasa Conturi //implementeaza interfetele class Conturi : IEnumerable,IEnumerator { //construim un vector ArrayList listaConturi = new ArrayList(); //variabila care va returna //pozitia curenta a enumeratorului private int pozitie = -1; //metoda pentru adaugarea unui obiect de tip // in lista public void AddEmployee(Cont c) { //adauga in lista listaConturi.Add(c); } //implementarea interfetei IEnumerable //va returna un enumerator public IEnumerator GetEnumerator() { return (IEnumerator)this; } //implementarea interfetei IEnumerator public bool MoveNext() { if (pozitie < listaConturi.Count - 1) { //incrementam ++pozitie; return true; } //nu mai sunt elemente in colectie return false; } public void Reset() { //pozitia initiala pozitie = -1; } public object Current { get { //returneaza elementul indicat de enumerator return listaConturi[pozitie]; } } }
In functia Main, creem colectia:
Conturi ContList = new Conturi(); //cconstruim obiecte de tip Cont Cont c1 = new Cont(1, "Cont#1", 1250.75); Cont c2 = new Cont(2, "Cont#2", 3131); Cont c3 = new Cont(3, "Cont#3", 400); //construim o colectie ContList.AddEmployee(c1); ContList.AddEmployee(c2); ContList.AddEmployee(c3);
Construim obiectul enumerator:
IEnumerator ContEnumerator = ContList.GetEnumerator();
Initial, obiectul enumerator este situat inaintea primului element al colectiei. Vom apela metoda Reset(), pentru a fi siguri de acest lucru:
ContEnumerator.Reset();
Parcurgem colectia:
//se invoca mai intai metoda MoveNext inainte de accesarea //Current, altfel va aparea exceptie la executie while (ContEnumerator.MoveNext()) { Console.WriteLine((Cont)ContEnumerator.Current); }
Atentie! Folosirea corecta a interfetei IEnumerator presupune mai intai apelul metodei MoveNext(), apoi accesarea proprietatii Current.
Obiectul enumerator nu are acces exclusiv asupra colectiei, parcurgerea cu ajutorul acestuia nu este thread-safe, pentru ca in acelasi timp alte fire de executie pot aduce modificari colectiei. O solutie ar fi blocarea colectiei in timpul parcurgerii.
In articolul urmator, vom discuta despre cateva clase din spatiul de nume System.IO.
Codul pentru acest articol este aici.