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ť ako čísla typu int
, tak používateľa, bude
problém s dátovým typom prvkov vo vnútri kolekcie. Existujú dve varianty,
ako tento problém vyriešiť a aj samotný .NET obsahuje kolekcie týchto dvoch
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
:
using System.Collections;
{CSHARP_CONSOLE}
ArrayList list = new ArrayList();
list.Add("item");
string item = (string)list[0];
Console.WriteLine(item);
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikácia
item
Po vytvorení zoznamu ArrayList
si do neho pridáme položku
typu string
. Aby sme túto položku mohli zo zoznamu 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á:
{CSHARP_CONSOLE}
List<string> list = new List<string>();
list.Add("item");
string item = list[0];
Console.WriteLine(item);
{/CSHARP_CONSOLE}
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 Genericity
. 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 jedna 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:
{CSHARP_CONSOLE} Class<int> instance = new Class<int>(10); bool alike = instance.Compare<string>("15"); Console.WriteLine(alike); {/CSHARP_CONSOLE}
{CSHARP_OOP} class Class<T> { private T variable; public Class(T variable) { this.variable = variable; } public bool Compare<T2>(T2 a) { return variable.Equals(a); } } {/CSHARP_OOP}
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 zoznamy (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é 1x (245.54 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#