Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

1. diel - Úvod do kolekcií a genericita

V dnešnej lekcii si povieme úvodnú teóriu ku kolekciám v C# .NET, ktoré si v tomto kurze podrobnejšie rozoberieme.

Kolekcia

Pojem kolekcia označuje súbor dát, ktoré sú väčšinou rovnakého typu a slúžia na špecifický účel. Počas seriálu sme sa už stretli s dvoma typmi kolekcií, bolo to pole a List. Kolekcií existuje veľké množstvo a hoci sa zvonku často tvária podobne, vo vnútri fungujú veľmi odlišne a vyberáme si ich podľa konkrétneho účelu. .NET disponuje veľkým množstvom predpripravených kolekcií, s ktorými sa v tejto sekcii postupne zoznámime a skúsime si s nimi pracovať.

Generické a všeobecné kolekcie

Keď sa zamyslíme nad tým, ako by sme si urobili vlastnú kolekciu, určite by sme po nejakej dobe dospeli k problému. Bol by ním dátový typ kolekcie. Chceli by sme si napr. naprogramovať vlastný List, vytvorili by sme triedu MyList.cs, do nej pridali príslušné metódy a všetko potrebné. Pretože však chceme, aby bola naša kolekcia univerzálna a vedela teda ukladať napr. ako int y, tak používateľa, bude problém s dátovým typom prvkov vo vnútri kolekcie. Existujú 2 varianty, ako tento problém vyriešiť a aj samotný .NET obsahuje kolekcie týchto 2 typov.

Všeobecné kolekcie

Keďže vieme, že všetky dátové typy majú ako predka triedu Object, môžeme prvky v našej kolekcii ukladať práve do tohto dátového typu. Do kolekcie teraz môžeme uložiť v podstate čokoľvek. Nevýhodou je, že sama kolekcia skutočný dátový typ prvkov nepozná a preto vie prvky navracať len ako všeobecné objekty. Po získaní prvku z kolekcie si ho teda musíme pretypovať.

Uveďme si príklad negenerickej kolekcie, je jej ArrayList:

ArrayList list = new ArrayList();
list.Add("item");

string item = (string)list[0];

Console.WriteLine(item);

Výstup programu:

Konzolová aplikácia
item

Po vytvorení listu si do neho pridáme položku typu string. Aby sme túto položku mohli z listu získať späť, treba ju na string spätne pretypovať.

Pre funkčnosť kódu musíme pridať using System.Collections;. Keďže tento menný priestor nie je v predvolenom projekte prítomný, napovedá nám to, že negenerické kolekcie nebudú tá obvyklá voľba.

Generické kolekcie

Generické kolekcie riešia problém s dátovým typom na úrovni jazyka C#. Zavádza tzv. genericitu. Zjednodušene povedané ide o možnosť špecifikovať dátový typ až vo chvíli vytvorenia inštancie. V triede samotnej kolekcie sa potom pracuje s generickým typom, ktorý slúži ako zástupca pre budúci dátový typ. Môžeme si to predstaviť tak, že sa generický typ v triede zmení napr. na string vo chvíli, keď vytvoríme jej inštanciu. Ide teda o možnosť triedy nejakým spôsobom parametrizovať.

Generický List už poznáme a onen dátový typ (parameter) sa generickým triedam špecifikuje v špicatých zátvorkách. Máme možnosť špecifikovať dátový typ iba raz, pri vytvorení kolekcie. Akékoľvek ďalšie pretypovanie odpadá:

List<string> list = new List<string>();
list.Add("item");

string item= list[0];

Console.WriteLine(item);

Výstup programu:

Konzolová aplikácia
item

Program funguje úplne rovnako, ako ten s negenerickou kolekciou ArrayList, ale čítať môžeme bez nepohodlného pretypovania.

Generické kolekcie nahradili kolekcie všeobecné a tie sa už príliš nepoužívajú. V kurze sa budeme venovať generickým kolekciám a ich negenerické verzie iba spomenieme.

Genericita

Genericita je samozrejme vlastnosť jazyka C# a my ju máme možnosť vo svojich triedach používať.

Zatiaľ sa nebudeme zaťažovať tvorbou vlastnej kolekcie. Vytvorme si triedu, ktorá bude jednoducho spravovať jednu premennú. Premenná bude generická, teda ľubovoľného dátového typu. Založte si nový projekt, konzolovú aplikáciu s názvom Generics. Pridajte si novú triedu, pomenujme ju teraz na študijné účely iba Class. V jej deklarácii pridáme generický parameter, ktorý pomenujeme T:

public class Class<T>
{

}

Generických parametrov môžeme zadať v špicatých zátvorkách viac, oddelíme ich čiarkou. Niekedy sa to môže hodiť, my sa s tým stretneme ďalej u generických slovníkov.

Presunieme sa do metódy Main(), kde si vytvoríme inštanciu našej triedy:

CLass<int> instance = new Class<int>();

Nezabudneme na špicaté zátvorky ako pri dátovom type, tak aj pri konštruktore. Teraz sme parametru T v tejto inštancii triedy určili dátový typ int. Rovnako si môžeme urobiť ďalšiu inštanciu tej istej triedy a parametra T dať úplne iný dátový typ, napr. string. Stačí nám teda 1 trieda pre viacero dátových typov.

Pokračujme a vytvorme si v triede atribút. T môžeme použiť ako bežný dátový typ:

private T variable;

Triede ešte dodáme konštruktor, ktorý premennú inicializuje:

public Class(T variable)
{
    this.variable = variable;
}

V Main() aktualizujeme vytvorenie inštancie:

Class<int> instance = new Class<int>(10);

Teraz inštancia obsahuje atribút variable, ktorý je typu int a nadobúda hodnotu 10.

Môžeme dokonca pridať metódu, ktorá bude mať navyše ďalší generický parameter (iný, než má trieda). Mohla by vyzerať napr. takto:

public bool Compare<T2>(T2 a)
{
    return variable.Equals(a);
}

Skúsime si teda porovnať náš int s nejakým iným typom:

    bool alike = instance.Compare<string>("15");
    Console.WriteLine(alike);

    class Class<T>
        {
        private T variable;

        public Class(T variable)
        {
                this.variable = variable;
        }

        public bool Compare<T2>(T2 a)
        {
                return variable.Equals(a);
        }
    }

Výstup programu:

False

Ďalšia konštrukcia

Pre úplnosť si ešte uveďme niekoľko konštrukcií.

Generický parameter triedy je možné bližšie špecifikovať, presnejšie obmedziť. Slúži na to kľúčové slovo where. Môžeme tak nastaviť, že udaný dátový typ musí napr. obsahovať rozhranie IComparable:

public class Class<T> where T: IComparable
{
    ...
}

Vďaka tomu môžeme na premenných typu T teraz vo vnútri triedy volať metódy z daného rozhrania. Samotné rozhranie môže opäť obsahovať generický parameter, aby sme generické typy mohli používať aj v hlavičkách jeho metód.

Keď uvedieme za where ešte new(), môžeme vo vnútri typ T inštanciovať. Taká malá továreň na inštancie ľubovoľného typu by mohla vyzerať takto:

public class Factory<T> where T : new()
{
    public T CreateInstance()
    {
        return new T();
    }
}

Pokiaľ máme vo where už nejaké rozhrania, new() dáme jednoducho medzi ne a oddelíme čiarkou.

Nakoniec si ukážme, ako môžeme typ parametra obmedziť z hľadiska dedičnosti:

public class Class<A, B, C> where A : B where B : C
{

}

Vyššie sme deklarovali triedu s tromi generickými parametrami, kde A je potomkom B a B je potomkom C.

V budúcej lekcii, Zoznam (List) pomocou poľa v C#, sa pozrieme na listy, predstavíme si rôzne implementácie tejto kolekcie a ich výhody a nevýhody.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 0x (245.05 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Všetky články v sekcii
Kolekcia a LINQ v C# .NET
Preskočiť článok
(neodporúčame)
Zoznam (List) pomocou poľa v C#
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
2 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity