Risipa de key_press | Programare

Programare .Net | Tehnici de programare | Tutoriale | Lectii si exemple

Risipa de key_press | Programare - Programare .Net | Tehnici de programare | Tutoriale | Lectii si exemple

Enumerarea si compararea colectiilor

   .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.

Category: Uncategorized

Your email address will not be published. Required fields are marked *

*