fel
le

Generic típusok

A generic (általános) típusok olyan objektumosztályok, amelyek típussal paraméterezhetőek.

Ezen osztályok tartalmaznak mezőket, metódusokat, propertyket, konstruktorokat, általában véve tehát "készen" vannak. Ezen osztályok vázként, sablonként (template) szolgálnak, konkrét példány készítése esetén még néhány hiányzó információt azonban ki kell tölteni.

A leggyakrabban történő felhasználásra példa a lista (System.Collections.Generic.List) osztály.

Lista készítése az ArrayList segítségével

Amikor listát készítünk, akkor általában az ArrayList osztályt használjuk fel. Az ArrayList azonban egy Object alaptípusú lista. Ez több helyen is problémát okoz.

Amikor adatokat kívánunk elhelyezni a listában, akkor az .Add(...) metódust kell használni, amely Object típusú, vagy azzal kompatibilis típusú értéket vár. Mivel az Object-el gyakorlatilag minden létező típus kompatibilis, ezért a listában mindenfajta érték elhelyezhető. Ez jellemzően több bajt okoz mint hasznot:

  • sosem lehetünk biztosak abban, hogy a listában csak általunk kívánatos típusú elemek vannak. A listaelemekre ezért nehéz biztonságos feldolgozó ciklust írni, hacsak nem választjuk annak alaptípusát is Object-re.
  • hogy biztosak lehessünk abban, hogy nem keveredik bele mást típusú adat, a listát általában elrejtjük a külvilág elől (meggátoljuk a direkt hozzáférést). Ugyanakkor az alapfunkciókat (hozzáadás, törlés, keresés, elem kiolvasása) a megfelelő típusra le kell programoznunk.

class Erdemjegyek
{
    protected ArrayList lista = new ArrayList();
    // ..................................................
    public void Hozzaad( int jegy )
    {
       lista.Add(jegy);
    }
    // ..................................................
    public void Torol( int index )
    {
       lista.RemoveAt( index );
    }
    // ..................................................
    public int Kiolvas( int index )
    {
       return (int)lista[index];
    }
    // ..................................................
    public int Elemszam()
    {
       return lista.Count;
    }
    // ..................................................
    public double Atlag()
    {
      int osszeg = 0;
      foreach(int x in lista)
        osszeg += x;
      return (double)osszeg/lista.Count;
    }
}
 

Amikor a lista valamely elemét kívánjuk feldolgozni, a fenti probléma miatt sok tennivalónk van:

  • a lista elemek típusát folyamatosan típusellenőrízni kell az IS operátorttal, ami lassítja a feldolgozást
  • a listaelem kiolvasott típusa Object, amit típuskényszerítenünk kell feldolgozáskor

További probléma value type alaptípusú lista esetén, hogy a berakás és kiolvasás műveletek során boxing-unboxing műveletek is zajlanak a háttérben (ami szintén lassítja a feldolgozást), valamint a memóriaigény is elég magas.

Lista készítése a Generic List segítségével

A C# 2.0-ban már helyet kapott a Generic típusok készítésének technológiája. Ez azt jelenti, hogy tudunk típussal paraméterezhető osztályt készíteni. Ennek során az osztály neve mellett meg kell adni egy betűjelet, amelynek segítségével ezen típusparaméterre hivatkozhatunk később:

class List<T>
{
    public void Add( T elem )
    {
      ...
    }

    public T this[index]
    {
       get
       {
          ...
       }
    }
}
// használata
List<int> lista = new List<int>();
 

A olyan, mint egy formális paraméterlista. Amikor az osztályt példányosítjuk, akkor meg kell adni egy létező típusnak a nevét (List). Ekkor úgy képzelhető el a dolog, mintha a T=int hajtódna végre, és az osztály forráskódja generálódik ezen beállítás mellett, vagyis a fenti kódból az alábbi képződik:

class List
{
    public void Add( int elem )
    {
      ...
    }

    public int  this[index]
    {
       get
       {
          ...
       }
    }
}
 

Ez úgyképzelhető el, hogy a T betű helyére mindenütt a megadott típusnév kerül (int), és az osztály kódja ennek megfelelően képződik és generálódik le. Az int típusú listának az az előnye, hogy garantáltan csak int típusú elemeket lehet benne tárolni, hiszen az .Add(...) metódus paramétertípusa ez lesz. Valamint kiolvasáskor a visszakapott elem típusa eleve int, nem kell típuskényszeríteni.

Ennek segítségével az Erdemjegy osztály készítése az alábbiakra egyszerűsödik le:

class Erdemjegyek
{
    public List<int> lista = new List<int>();
    // ..................................................
    public double Atlag()
    {
      int osszeg = 0;
      foreach(int x in lista)
        osszeg += x;
      return (double)osszeg/lista.Count;
    }
}
 

Nyugodtan kitehetjük publikusra a lista nevű mezőt, és nem kell kiváltó metódusokat készíteni az ArrayList típusbiztonságának megőrzése céljából.

További Generic típusok

A System.Collections.Generic névtében a BCL 2.0 változata több, hasonlóan általános, típusbiztos osztályt tartalmaz:

  • List: lista
  • Stack: verem
  • Queue: sor
  • Dictionary : szótár (név-érték páros)

Saját Generic típus készítése

A technológia természetesen rendelkezésre áll a saját osztályok fejlesztéséhez is. Amennyiben hasonló jellegű osztályt fejlesztünk, mi is élhetünk a lehetőséggel. Az osztályok egyébként nem csak egy, hanem tetszőlegesen sok típust is fogadhatnak paraméterként. Erre példa a Dictionary generic osztály, amely két típussal paraméterezhető. Az első típus a "név" típusát határozza meg, a második típus pedig az "érték" típusát írja le. A Dictionary osztály ilyen név-érték típuspárokat tartalmaz. A "név" általában egy string, de lehet egy GUID is, vagy egy egyszerű azonosító egész szám is. Az "érték" pedig feladattól függően bármi lehet, akár saját típusú példányok is.

A megkapott típusparamétert felhasználhatjuk metódusok paraméter-típusainak leírására, a metódus visszatérési típusának jelölésére, propertyk esetén hasonlóan, de akár mező vagy változó típusának megadására is:

class Akarmi<X>
{
 // ilyen típusú mező deklarálása
 protected X mezo;
 
 // ilyen visszatérési típusú metódus
 public X valami()
 {
   return ....;
 }
 
 // ilyen típusú property
 public X prop
 {
   // a 'get'-nek ilyen típusú értékkel kell majd visszatérnie
   get
   {
   }
   // a 'set' belsejében a 'value' is ilyen típusú lesz
   set
   {
   }
 }
 
 public void nemtommi()
 {
   // ilyen típusú változó
   X a;
   ...
 }
}
 

Saját Generic metódus készítése

Nem csak osztályok lehetnek generic-ek, hanem kisebb egységek, akár metódusok is. Az alábbi példában egy olyan Csere metódust mutatunk be, amely megadott típusú két változó cseréjét hajtja végre segédváltozón keresztül. A két cserélendő változó típusát meg kell adni használatkor. A metódus egy megfelelő típusú segédváltozót deklarál a csere végrehajtásához.

class JoKisCsereloOsztaly
{
    public void Csere<T>( ref T a, ref T b)
    {
      T seged = a;
      a=b;
      b=seged;
    }
}

// alkalmazása
JoKisCsereloOsztaly j = new JoKisCsereloOsztaly();
string alfa="alma", beta="barack";
j.Csere<string>(ref alfa, ref beta);
 
Hernyák Zoltán
2013-03-17 18:47:32