Clase și funcții generice

Genericele sunt șabloane (templates) sau modele care ajută la reutilizarea codului. Ele descriu clase și metode care pot lucra într-o manieră uniformă cu tipuri de valori diferite.

Ele permit definirea de funcționalități și metode care se adaptează la tipurile parametrilor pe care îi primesc, ceea ce permite construirea unui șablon.

Singura diferență față de declararea în mod obișnuit a unei clase, este prezența caracterelor <și>, care permit definirea tipului pe care stiva îl va avea, ca și cum ar fi un parametru al clasei.

La instanțierea clasei trebuie să declarăm tipul datelor utilizate.

Tipurile generice (parametrizate) permit construirea de clase, structuri, interfețe, delegați sau metode care sunt parametrizate printr-un tip pe care îl pot stoca sau manipula.

Să considerăm clasa Stiva care permite stocarea de elemente. Această clasă are două metode Push() care permite introducerea de elemente și Pop() care permite extragerea de elemente din stivă.

public class Stiva<TipElement> //clasa generica
{
    private TipElement[] element;
    public void Push(TipElement data)
    {
    // code corespunzator introducerii de elemente
    }
    public TipElement Pop()
    {
    // code corespunzator extragerii de elemente
    }
}
Stiva<char> StivaMea = new Stiva<char>();
StivaMea.Push("a");
char x = StivaMea.Pop();
 Tipurile parametrizate pot fi aplicate claselor și interfețelor.
interface IGeneric1<T>
{ }
class ClassGeneric1<UnTip, Altul>
{ }
class ClassInt1 : ClassGeneric1<intint>
{ }
class ClassInt2<T> : ClassGeneric1<int, T>
{ }
class ClassInt3<T, U> : ClassGeneric1<int, U>
{ }
 Tipurile parametrizate se pot aplica metodelor
class clA
{
    public void methode1<T>()
    {
    }
    public T[] methode2<T>()
    {
        return new T[10];
    }
}
 Dorim să implementăm o clasă Stiva care să permită adăugarea și extragerea de elemente. Pentru a simplifica problema, vom considera că stiva nu poate conține decât un anumit număr de elemente, ceea ce ne va permite să utilizăm tablouri în C#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Exemplu
{
    class Stiva
    {
        private object[] m_ItemsArray;
        private int m_Index = 0;
        public const int MAX_SIZE = 100;
        public Stiva() 
        { 
            m_ItemsArray = new object[MAX_SIZE]; 
        }
        public Object Pop()
        {
            if (m_Index == 0)
            throw new InvalidOperationException("Nu putem extrage un element dintr-o stiva vida.");
            return m_ItemsArray[--m_Index];
        }
        public void Push(Object item)
        {
            if (m_Index == MAX_SIZE)
            throw new StackOverflowException("Nu se poate adauga un element: stiva este plina.");
            m_ItemsArray[m_Index++= item;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Stiva stiva = new Stiva();
            stiva.Push(1234);
            int numar = (int)stiva.Pop();
        }
    }
}
 Implementarea suferă de câteva probleme:
  • elementele clasei Stiva trebuie să fie convertite explicit
  • atunci când se folosește clasa Stiva cu elemente de tip valoare, se realizează implicit o operație de boxing cu inserarea unui element și o operație de tip unboxing cu recuperarea unui element
  • dorim să introducem în stivă elemente de tipuri diferite în aceeași instanță a clasei Stiva. Acest lucru va duce la probleme de convertire care vor fi descoperite la execuție.

Deoarece problema conversiei nu este detectată la compilare, va produce o excepție la execuție. Din acest motiv, codul nu este type-safe.

Pentru a rezolva aceste neajunsuri s-ar putea implementa un cod pentru stive cu elemente de tip int, alt cod pentru elemente de tip șir de caractere. Acest lucru duce la dublarea unor porțiuni din cod. Acest lucru se va rezolva cu ajutorul tipurilor generice.

C# permite rezolvarea unor astfel de probleme introducând tipurile generice. Concret putem implementa o listă de elemente de tip T, lăsând libertatea utilizatorului să specifice tipul T la instanțierea clasei.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Exemplu
{
    class Stiva<T>
    {
        private T[] m_ItemsArray;
        private int m_Index = 0;
        public const int MAX_SIZE = 100;
        public Stiva() { m_ItemsArray = new T[MAX_SIZE]; }
        public T Pop()
        {
            if (m_Index == 0)
            throw new InvalidOperationException("Nu putem extrage un element dintr-o stiva vida.");
            return m_ItemsArray[--m_Index];
        }
        public void Push(Object item)
        {
            if (m_Index == MAX_SIZE)
            throw new StackOverflowException("Nu se poate adauga un element: stiva este plina.");
            m_ItemsArray[m_Index++= item;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Stiva<int> stiva = new Stiva<T>();
            stiva.Push(1234);
            int numar = stiva.Pop(); //nu mai este necesar cast
            Stiva<string> sstiva = new Stiva<string>();
            sstiva.Push("4321");
            string sNumar = sstiva.Pop();
        }
    }
}

Leave a comment