SlideShare a Scribd company logo
1 of 47
Download to read offline
Pokročilé techniky
programováníPřednáška 03 – Genericita
Genericita
„Obdoba šablon v jazyce C++“
Deklarace typu, který pracuje s typovým parametrem, se nazývá deklarace generického
typu. Jazyk C# poskytuje následující generické typy:
generické třídy,
generické struktury,
generická rozhraní,
generické delegáty.
Problém genericity
Mějme datovou strukturu zásobník pracující s prvky typu object.
class Zasobnik
{
object[] pole = new object[100];
int pocet;
public void Vloz(object obj)
{
if (pocet < pole.Length) pole[pocet++] = obj;
}
public object Odeber()
{
if (pocet > 0) return pole[--pocet];
return null;
}
}
Problém genericity
Do uvedeného zásobníku lze přidat hodnotu libovolného datového typu bez
nutnosti přetypování. Při odebrání prvku se musí provést přetypování:
Zasobnik zasobnik = new Zasobnik();
zasobnik.Vloz(1);
zasobnik.Vloz(2);
int cislo = (int)zasobnik.Odeber();
Generické třídy a struktury
Pro generické struktury platí stejná pravidla jako pro generické třídy.
Syntaxe generické třídy:
deklarace generické třídy:
modifikátorynep partialnep class identifikátor seznam_typových_parametrů
předeknep omezení_typových_parametrůnep tělo ;nep
seznam_typových_parametrů:
< typové_parametry >
Generické třídy - příklad
Příklad demonstruje deklaraci generického zásobníku a jeho použití pro zásobník
celých čísel.
https://goo.gl/9a7zf4
Uvedený zásobník je typově bezpečný, tj. může pracovat pouze s celými čísly.
Žádné přetypování při použití metody Odeber není nutné.
Generické třídy
Generické typy mohou být „přetíženy“. Lze deklarovat generické typy se stejným
jménem lišící se počtem typových parametrů. Lze také deklarovat stejně
pojmenovanou negenerickou a generickou třídu. Např.
class C { }
class C<T> { } // OK
class C<T, U> { } // OK
struct C<A, B> { } // Chyba – existuje C<T, U>
Generické třídy – použití gen. typu
Nesvázaný generický typ
Konstruovaný generický typ
Generické třídy – skutečný gen. typ
Skutečným typovým parametrem generického typu může být nejen skutečný
datový typ, ale i konstruovaný typ, např. zásobník zásobníků int
GenZasobnik<GenZasobnik<int>> nebo formální typový parametr, pokud je
tento generický typ použit uvnitř deklarace jiného generického typu.
Konstruovaný gen. typ
otevřený konstruovaný typ (angl. open constructed type) – používá alespoň jeden
formální typový parametr nějakého generického typu, např. GenZasobnik<T>.
zavřený konstruovaný typ (angl. closed constructed type) – má všechny typové
parametry nahrazeny skutečnými datovými typy, např. GenZasobnik<int>,
GenZasobnik<GenZasobnik<string>>.
Konstruovaný gen. Typ – příklad
Příklad obsahuje fragment deklarace třídy Slovnik a její použití. Třída Slovnik
má dva typové parametry, představující typ klíče a typ hodnoty. Tyto parametry jsou
použity jako skutečné typové parametry dvou datových složek generického typu
Vektor.
https://goo.gl/dDzCKw
V uvedeném příkladu je typ Slovnik<int, string> zavřený konstruovaný
typ a typy Vektor<K> a Vektor<H> jsou otevřené konstruované typy.
Vnořené třídy
Třída vnořená do generické třídy
je automaticky také generická,
protože má přístup k typovým
parametrům vnější třídy. Opačně
toto pravidlo neplatí. To
znamená, že vnořená generická
třída může být součástí generické
i negenerické třídy.
class A<T>
{
public class B {
public T x; // OK
}
}
class Program
{
static void Main(string[] args) {
A<int>.B b = new A<int>.B();
b.x = 10; // OK
}
}
Statické datové složky
Statická datová složka
generické třídy je společná
pro všechny instance
stejného zavřeného
konstruovaného typu. Není
společná pro odlišné
zavřené konstruované typy.
Toto pravidlo platí bez
ohledu na to, zda daná
statická datová složka je
skutečného datového typu
nebo typu typového
parametru.
class A<T> {
public static int Pocet;
private T y;
public A(T y) { this.y = y; Pocet++; }
}
class Program {
static void Main(string[] args) {
A<int> a1 = new A<int>(10);
A<int> a2 = new A<int>(20);
A<double> a3 = new A<double>(1.5);
Console.WriteLine(A<int>.Pocet);
Console.WriteLine(A<double>.Pocet);
Console.ReadKey();
}
}
Statický konstruktor
Statický konstruktor generické třídy je volán pro každý zavřený konstruovaný typ
zvlášť. Je volán v okamžiku, kdy je poprvé vytvořena instance daného zavřeného
konstruovaného typu nebo kdy je poprvé použita některá statická složka daného
zavřeného konstruovaného typu.
Je vhodným místem pro kontrolu omezení typového parametru, které nelze
specifikovat v části omezení typových parametrů. Taková kontrola se však provede
až za běhu programu.
Statický konstruktor – příklad
Generická třída A požaduje, aby její typový parametr byl výčtového typu. Toto
omezení kontroluje ve svém statickém konstruktoru a v případě chyby vyvolá
výjimku.
https://goo.gl/5MABpK
Implementace genericity
Genericita je v .NET podporována na úrovni mezijazyka IL a modulem CLR. Při překladu
generické třídy do IL, bude v IL uveden jen jeden kód generické třídy s formálními typovými
parametry.
Až překladač JIT překládá generickou třídu do nativního kódu nahrazením formálních
typových parametrů skutečnými parametry. Výsledný kód závisí na typu skutečného
typového parametru:
Pro každý hodnotový typ vytvoří samostatnou třídu, avšak žádné zabalení a vybalení
instance hodnotového typu se neprovádí.
Pro všechny referenční typy vytvoří jednu třídu, v níž bude typový parametr nahrazen
typem object, avšak žádné přetypování se neprovádí.
Implementace genericity
Při použití generické třídy místo normální třídy s typem object se uvádí zvýšení
výkonu:
u hodnotových typů až o 200 % (neprovádí se zabalení a vybalení),
u referenčních typů až o 100 % (neprovádí se přetypování).
Implicitní hodnoty
Proměnným, jejichž typ je dán typovým parametrem, nelze přiřadit hodnotu null.
- skutečným typovým parametrem může být také hodnotový typ a hodnota null je
povolena pouze pro referenční typy.
K tomuto účelu lze použít výraz default (angl default value expression).
výraz_default:
default ( typ )
Typ – jméno typu nebo formálního typového parametru generického typu.
Implicitní hodnoty
Pokud typ reprezentuje referenční typ, výsledkem výrazu je hodnota null. Pokud
představuje hodnotový typ, výsledkem je implicitní hodnota pro daný hodnotový
typ, což je:
0 pro číselné a výčtové typy,
false pro typ bool,
null pro nulovatelné typy,
výsledek volání implicitního konstruktoru pro uživatelem definované struktury –
nastavení implicitních hodnot pro všechny datové složky.
Generická rozhraní
deklarace generického rozhraní:
modifikátorynep partialnep interface jméno seznam_typových_parametrů předeknep
omezení_typových_parametrůnep { složky_rozhraní } ;nep
Typové parametry a omezení typových parametrů mají stejný význam jako u
deklarace generické třídy nebo struktury.
Generická rozhraní – příklad
Příklad demonstruje deklaraci generického rozhraní zásobníku a jeho implicitní
implementaci v generické třídě GenZasobnik.
https://goo.gl/hFjMZe
Generická rozhraní
Generické rozhraní samozřejmě může implementovat i negenerický typ. Např.
class IntZasobnik : IZasobnik<int>
{
public void Vloz(int obj) { ... }
public int Odeber() { ... }
}
Generická rozhraní
Explicitně implementované složky generického rozhraní se kvalifikují
konstruovaným generickým rozhraním, např.
class ExplicitGenZasobnik<T> : IZasobnik<T>
{
// ...
void IZasobnik<T>.Vloz(T obj) { ... }
T IZasobnik<T>.Odeber() { ... }
}
Generické delegáty
deklarace generického delegátu:
modifikátornep delegate typ jméno seznam_typových_parametrů (
seznam_parametrůnep ) omezení_typových_parametrůnep ;
Typové parametry a omezení typových parametrů mají stejný význam jako u
deklarace generické třídy nebo struktury.
Generické delegáty – příklad
V příkladu je použita generická třída GenZasobnik z předchozího příkladu. Obsahuje
navíc metodu Najdi, která hledá první výskyt prvku v zásobníku, pro který zadaný
predikát vrací hodnotu true. Predikát je realizován generickým delegátem.
Zásobník je v hlavní metodě použit pro uložení řetězců znaků načtených z klávesnice.
Pomocí metody Najdi se hledá první výskyt řetězce, který začíná zadaným textem.
Skutečným parametrem metody Najdi je výraz lambda.
Příkaz volání metody Najdi by bylo možné také zapsat pomocí dvou příkazů např. s
použitím anonymní metody:
Predikat<string> predikat =
delegate(string hodnota) { return hodnota.StartsWith(pocatek); };
string text = zasobnik.Najdi(predikat);
https://goo.gl/b3OMXG
Generické metody
Generická metoda je metoda, jejíž deklarace zahrnuje seznam typových parametrů
a případné jejich omezení. Její syntaxe deklarace je následující:
deklarace generické metody:
modifikátorynep typ jméno seznam_typových_parametrů ( seznam_parametrůnep )
omezení_typových_parametrůnep tělo
Typové parametry a omezení typových parametrů mají stejný význam jako u
deklarace generické třídy nebo struktury.
Generické metody
Generická metoda obsahuje za jménem metody v lomených závorkách formální
typové parametry oddělené čárkou. Pro jednotlivé typové parametry může
obsahovat omezení, které se uvádějí až za seznamem formálních parametrů
metody.
Formální typový parametr generické metody lze použít jako návratový typ, typ
formálního parametru metody nebo v těle metody.
Generická metoda může být deklarována v generické i negenerické třídě, struktuře
nebo rozhraní. Je-li deklarována v rámci generického typu, mohou být v ní použity i
typové parametry tohoto generického typu.
Generické metody – příklad
Třída Obecne obsahuje statickou generickou metodu Vymena, která vymění
hodnoty dvou proměnných.
https://goo.gl/tYajjI
Generické metody
Při přetěžování metod se do signatury metody nezahrnuje ani omezení typových
parametrů ani jména typových parametrů. Rozhodují jen pravidla platná pro
přetěžování negenerických metod, tj. počet, typ, pořadí a způsob předávání
parametrů. Rozlišuje se ale stejně pojmenovaná generická a negenerická metoda.
Např. nemohou být deklarovány dvě uvedené metody f.
class A
{
void f<T>(T x) { }
void f<U>(U x) { } // Chyba – existuje již f<T>
}
Generické metody
Předefinovaná virtuální generická metoda nesmí obsahovat žádná omezení
typových parametrů. Zdědí tato omezení z původní virtuální metody.
Do instance delegátu lze přiřadit i odkaz na generickou metodu. Generická metoda
se v takovém případě uvádí jejím jménem, za nímž se v lomených závorkách
specifikují skutečné typové parametry. Typové parametry se mohou vynechat,
potom se odvodí z parametrů delegátu, do kterého se generická metoda přiřazuje.
https://goo.gl/0ia1wG
Direktiva using
Direktivu using pro deklaraci nového jména pro daný typ lze použít jen pro zavřený konstruovaný
typ. Např.
using SlovnikIS = Slovnik<int, string>; // OK
using SlovnikX = Slovnik; // Chyba
class Program {
static void Main(string[] args) {
SlovnikIS slovnik = new SlovnikIS();
slovnik.Pridej(1, "text");
// ...
} }
Omezení typových parametrů
V deklaraci generického typu nebo generické metody lze specifikovat omezení pro
typové parametry.
Omezení je nutné specifikovat pro typový parametr, pokud je potřebné přistupovat
k jeho složkám, např. volat jeho metodu. Pokud typový parametr nemá
specifikována žádná omezení, lze s ním pracovat, jako by byl typu object.
Omezení typových parametrů –
syntaxespecifikace_omezení_typového_parametru
specifikace_omezení_typového_parametru omezení_typových_parametrů
specifikace_omezení_typového_parametru:
where typový_parametr : omezení_typového_parametru
omezení_typového_parametru:
primární_omezení
seznam_sekundárních_omezení
konstruktorové_omezení
seznam_omezení
Seznam omezení – seznam maximálně tří druhů omezení oddělených čárkou v pořadí
primární, sekundární a konstruktorové.
Primární omezení
Primární omezení pro typový parametr má následující syntaxi:
typ_třídy
class
struct
Typ_třídy – jméno typu třídy.
Omezení – příklad
Je dána třída entity Entita, mající vlastnost Id. Od ní je odvozena třída uživatele
Uzivatel. Dále je deklarována generická třída EntitaSeznam zapouzdřující
pole entit a obsahující metodu NajdiId, která hledá index entity v poli podle
zadaného Id. Aby se mohlo v této metodě přistupovat k vlastnosti Id prvku pole,
musí se pro typový parametr T specifikovat primární omezení určující, že T musí být
typu Entita. Metoda Main demonstruje použití seznamu entit na seznamu
uživatelů.
https://goo.gl/Y2l05I
Omezení – příklad – varianty
Pokud by třída EntitaSeznam neobsahovala specifikaci omezení pro typový
parametr, příkaz #1 by mohl být nahrazen příkazem
if ((item as Entita).Id == id) return index; // OK
Avšak následující příkaz by byl chybný:
if (((Entita)item).Id == id) return index; // Chyba
Sekundární omezení
seznam_sekundárních_omezení:
typ_rozhraní
typový_parametr
seznam_sekundárních_omezení , typ_rozhraní
seznam_sekundárních_omezení , typový_parametr
Typ_rozhraní – jméno rozhraní.
Typový_parametr – jméno jiného formálního typového parametru.
Sekundární omezení
Seznam sekundárních omezení může obsahovat seznam rozhraní oddělených
čárkou, které daný typový parametr musí implementovat nebo musí být přímo typu
uvedeného rozhraní. Dále může obsahovat jméno jiného typového parametru,
kterému má být daný typový parametr roven nebo musí být jeho potomkem.
Např. následující generická třída A vyžaduje, aby typový parametr U byl stejného
typu jako typový parametr T nebo byl jeho potomkem a zároveň, aby implementoval
rozhraní ICloneable.
class A<T, U> where U : T, ICloneable { }
Sekundární omezení – příklad
Předchozí přiklad je rozšířen o seřazení seznamu entit podle Id. Řazení provádí
metoda Serad třídy EntitaSeznam, v níž se porovnávají dvě entity pomocí
metody ComapareTo rozhraní IComparable. Toto rozhraní tudíž musí třída
Entita implementovat a musí být specifikováno v omezení typového parametru
třídy EntitaSeznam.
https://goo.gl/g36Crj
Sekundární omezení – příklad –
varianty
Pokud by třída EntitaSeznam neobsahovala specifikaci omezení
IComparable, příkaz #2 by mohl být nahrazen jedním z následujících příkazů:
if (((IComparable)pole[j]).CompareTo(pole[j + 1]) > 0) {
if ((pole[j] as IComparable).CompareTo(pole[j + 1]) > 0) {
Sekundární omezení – příklad –
varianty
Při použití rozhraní IComparable se provádí přetypování mezi typem object a
skutečným typem prvku pole. Pokud by prvkem pole byl hodnotový typ, provádělo
by se zabalení a vybalení, což snižuje výkon aplikace. Proto od verze .NET 2.0 je k
dispozici i generické rozhraní IComparable<T> deklarované v prostoru jmen
System následovně:
public interface IComparable<T>
{ int CompareTo(T other) }
https://goo.gl/f5poNk
Konstruktorové omezení
konstruktorové_omezení:
new ( )
Konstruktorové omezení specifikuje, že typový parametr reprezentuje referenční
typ, který má veřejný bezparametrický konstruktor. Jen pomocí tohoto konstruktoru
lze v generickém typu nebo metodě vytvořit novou instanci typu reprezentovaného
formálním typovým parametrem.
Konstruktorové omezení
Příklad:
class A<T> where T : new() {
T x;
public A() {
x = new T(); // #1
}
}
Konstruktorové omezení
class B<T> where T : struct {
T x;
public B() {
x = new T(); // #2
}
}
Doporučení pro pojmenování
Formální typové parametry by měly být pojmenovány podle následujících pravidel:
• Název typového parametru by měl začínat písmenem T.
• Lze-li typový parametr nahradit libovolným typem, protože pro něj není
specifikováno žádné omezení a zároveň se používá pouze jeden typový parametr,
lze použít samotné písmeno T.
Doporučení pro pojmenování
•Je-li pro typový parametr specifikováno omezení, např. že musí implementovat nějaké
rozhraní nebo musí být odvozen od dané třídy, nebo jestliže se používá více typových
parametrů, měly by se použít popisné názvy.
Např. pokud typ typového parametru musí implementovat rozhraní IComparable, měl by
se jmenovat TComparable, nebo pokud typ typového parametru musí být odvozen od
třídy Entita, měl by se jmenovat TEntita:
class TridenySeznam<TComparable> where TComparable :
IComparable
class EntitaSeznam<TEntita> where TEntita : Entita
class Dictionary<TKey,TValue> { }
Q & A

More Related Content

More from Jan Hřídel

More from Jan Hřídel (10)

INPTP Rekapitulace
INPTP Rekapitulace INPTP Rekapitulace
INPTP Rekapitulace
 
C# - Vícevláknové aplikace
C# - Vícevláknové aplikaceC# - Vícevláknové aplikace
C# - Vícevláknové aplikace
 
ASP.NET MVC
ASP.NET MVCASP.NET MVC
ASP.NET MVC
 
INPTP wpf
INPTP   wpfINPTP   wpf
INPTP wpf
 
Aplikační nastavení v .NET
Aplikační nastavení v .NETAplikační nastavení v .NET
Aplikační nastavení v .NET
 
ADO.NET
ADO.NETADO.NET
ADO.NET
 
INPTP přednáška 01a 2016
INPTP přednáška 01a 2016INPTP přednáška 01a 2016
INPTP přednáška 01a 2016
 
Startup investor pitch
Startup investor pitchStartup investor pitch
Startup investor pitch
 
Nástěnka 01
Nástěnka 01Nástěnka 01
Nástěnka 01
 
Nástěnka 02
Nástěnka 02Nástěnka 02
Nástěnka 02
 

Recently uploaded

Project Restart 2024: Karel Smutný - Specializace patří do 19. století
Project Restart 2024: Karel Smutný - Specializace patří do 19. stoletíProject Restart 2024: Karel Smutný - Specializace patří do 19. století
Project Restart 2024: Karel Smutný - Specializace patří do 19. stoletíTaste
 
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?Taste
 
Martina Košanová: Komunikace s problémovými uživateli knihoven
Martina Košanová: Komunikace s problémovými uživateli knihovenMartina Košanová: Komunikace s problémovými uživateli knihoven
Martina Košanová: Komunikace s problémovými uživateli knihovenÚISK FF UK
 
Project Restart 2024: Jiří Langr - Mytologie projektů
Project Restart 2024: Jiří Langr - Mytologie projektůProject Restart 2024: Jiří Langr - Mytologie projektů
Project Restart 2024: Jiří Langr - Mytologie projektůTaste
 
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...Taste
 
Project Restart 2024: Pavel Minář - Procesy pro lepší projekty
Project Restart 2024: Pavel Minář - Procesy pro lepší projektyProject Restart 2024: Pavel Minář - Procesy pro lepší projekty
Project Restart 2024: Pavel Minář - Procesy pro lepší projektyTaste
 
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...Taste
 
Project Restart 2024: Lenka Auerová - Budování holistické organizace
Project Restart 2024: Lenka Auerová - Budování holistické organizaceProject Restart 2024: Lenka Auerová - Budování holistické organizace
Project Restart 2024: Lenka Auerová - Budování holistické organizaceTaste
 

Recently uploaded (8)

Project Restart 2024: Karel Smutný - Specializace patří do 19. století
Project Restart 2024: Karel Smutný - Specializace patří do 19. stoletíProject Restart 2024: Karel Smutný - Specializace patří do 19. století
Project Restart 2024: Karel Smutný - Specializace patří do 19. století
 
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?
Project Restart 2024: Jan Řezáč - Nahradí AI projektové manažery?
 
Martina Košanová: Komunikace s problémovými uživateli knihoven
Martina Košanová: Komunikace s problémovými uživateli knihovenMartina Košanová: Komunikace s problémovými uživateli knihoven
Martina Košanová: Komunikace s problémovými uživateli knihoven
 
Project Restart 2024: Jiří Langr - Mytologie projektů
Project Restart 2024: Jiří Langr - Mytologie projektůProject Restart 2024: Jiří Langr - Mytologie projektů
Project Restart 2024: Jiří Langr - Mytologie projektů
 
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...
Project Restart 2024: Hana Březinová - Psychologické tipy pro práci s lidmi n...
 
Project Restart 2024: Pavel Minář - Procesy pro lepší projekty
Project Restart 2024: Pavel Minář - Procesy pro lepší projektyProject Restart 2024: Pavel Minář - Procesy pro lepší projekty
Project Restart 2024: Pavel Minář - Procesy pro lepší projekty
 
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...
Project Restart 2024: Martin Vasquez - Inteligence je schopnost reagovat na z...
 
Project Restart 2024: Lenka Auerová - Budování holistické organizace
Project Restart 2024: Lenka Auerová - Budování holistické organizaceProject Restart 2024: Lenka Auerová - Budování holistické organizace
Project Restart 2024: Lenka Auerová - Budování holistické organizace
 

INPTP přednáška 03 2016

  • 2. Genericita „Obdoba šablon v jazyce C++“ Deklarace typu, který pracuje s typovým parametrem, se nazývá deklarace generického typu. Jazyk C# poskytuje následující generické typy: generické třídy, generické struktury, generická rozhraní, generické delegáty.
  • 3. Problém genericity Mějme datovou strukturu zásobník pracující s prvky typu object. class Zasobnik { object[] pole = new object[100]; int pocet; public void Vloz(object obj) { if (pocet < pole.Length) pole[pocet++] = obj; } public object Odeber() { if (pocet > 0) return pole[--pocet]; return null; } }
  • 4. Problém genericity Do uvedeného zásobníku lze přidat hodnotu libovolného datového typu bez nutnosti přetypování. Při odebrání prvku se musí provést přetypování: Zasobnik zasobnik = new Zasobnik(); zasobnik.Vloz(1); zasobnik.Vloz(2); int cislo = (int)zasobnik.Odeber();
  • 5. Generické třídy a struktury Pro generické struktury platí stejná pravidla jako pro generické třídy. Syntaxe generické třídy: deklarace generické třídy: modifikátorynep partialnep class identifikátor seznam_typových_parametrů předeknep omezení_typových_parametrůnep tělo ;nep seznam_typových_parametrů: < typové_parametry >
  • 6. Generické třídy - příklad Příklad demonstruje deklaraci generického zásobníku a jeho použití pro zásobník celých čísel. https://goo.gl/9a7zf4 Uvedený zásobník je typově bezpečný, tj. může pracovat pouze s celými čísly. Žádné přetypování při použití metody Odeber není nutné.
  • 7. Generické třídy Generické typy mohou být „přetíženy“. Lze deklarovat generické typy se stejným jménem lišící se počtem typových parametrů. Lze také deklarovat stejně pojmenovanou negenerickou a generickou třídu. Např. class C { } class C<T> { } // OK class C<T, U> { } // OK struct C<A, B> { } // Chyba – existuje C<T, U>
  • 8. Generické třídy – použití gen. typu Nesvázaný generický typ Konstruovaný generický typ
  • 9. Generické třídy – skutečný gen. typ Skutečným typovým parametrem generického typu může být nejen skutečný datový typ, ale i konstruovaný typ, např. zásobník zásobníků int GenZasobnik<GenZasobnik<int>> nebo formální typový parametr, pokud je tento generický typ použit uvnitř deklarace jiného generického typu.
  • 10. Konstruovaný gen. typ otevřený konstruovaný typ (angl. open constructed type) – používá alespoň jeden formální typový parametr nějakého generického typu, např. GenZasobnik<T>. zavřený konstruovaný typ (angl. closed constructed type) – má všechny typové parametry nahrazeny skutečnými datovými typy, např. GenZasobnik<int>, GenZasobnik<GenZasobnik<string>>.
  • 11. Konstruovaný gen. Typ – příklad Příklad obsahuje fragment deklarace třídy Slovnik a její použití. Třída Slovnik má dva typové parametry, představující typ klíče a typ hodnoty. Tyto parametry jsou použity jako skutečné typové parametry dvou datových složek generického typu Vektor. https://goo.gl/dDzCKw V uvedeném příkladu je typ Slovnik<int, string> zavřený konstruovaný typ a typy Vektor<K> a Vektor<H> jsou otevřené konstruované typy.
  • 12. Vnořené třídy Třída vnořená do generické třídy je automaticky také generická, protože má přístup k typovým parametrům vnější třídy. Opačně toto pravidlo neplatí. To znamená, že vnořená generická třída může být součástí generické i negenerické třídy. class A<T> { public class B { public T x; // OK } } class Program { static void Main(string[] args) { A<int>.B b = new A<int>.B(); b.x = 10; // OK } }
  • 13. Statické datové složky Statická datová složka generické třídy je společná pro všechny instance stejného zavřeného konstruovaného typu. Není společná pro odlišné zavřené konstruované typy. Toto pravidlo platí bez ohledu na to, zda daná statická datová složka je skutečného datového typu nebo typu typového parametru. class A<T> { public static int Pocet; private T y; public A(T y) { this.y = y; Pocet++; } } class Program { static void Main(string[] args) { A<int> a1 = new A<int>(10); A<int> a2 = new A<int>(20); A<double> a3 = new A<double>(1.5); Console.WriteLine(A<int>.Pocet); Console.WriteLine(A<double>.Pocet); Console.ReadKey(); } }
  • 14. Statický konstruktor Statický konstruktor generické třídy je volán pro každý zavřený konstruovaný typ zvlášť. Je volán v okamžiku, kdy je poprvé vytvořena instance daného zavřeného konstruovaného typu nebo kdy je poprvé použita některá statická složka daného zavřeného konstruovaného typu. Je vhodným místem pro kontrolu omezení typového parametru, které nelze specifikovat v části omezení typových parametrů. Taková kontrola se však provede až za běhu programu.
  • 15. Statický konstruktor – příklad Generická třída A požaduje, aby její typový parametr byl výčtového typu. Toto omezení kontroluje ve svém statickém konstruktoru a v případě chyby vyvolá výjimku. https://goo.gl/5MABpK
  • 16. Implementace genericity Genericita je v .NET podporována na úrovni mezijazyka IL a modulem CLR. Při překladu generické třídy do IL, bude v IL uveden jen jeden kód generické třídy s formálními typovými parametry. Až překladač JIT překládá generickou třídu do nativního kódu nahrazením formálních typových parametrů skutečnými parametry. Výsledný kód závisí na typu skutečného typového parametru: Pro každý hodnotový typ vytvoří samostatnou třídu, avšak žádné zabalení a vybalení instance hodnotového typu se neprovádí. Pro všechny referenční typy vytvoří jednu třídu, v níž bude typový parametr nahrazen typem object, avšak žádné přetypování se neprovádí.
  • 17. Implementace genericity Při použití generické třídy místo normální třídy s typem object se uvádí zvýšení výkonu: u hodnotových typů až o 200 % (neprovádí se zabalení a vybalení), u referenčních typů až o 100 % (neprovádí se přetypování).
  • 18. Implicitní hodnoty Proměnným, jejichž typ je dán typovým parametrem, nelze přiřadit hodnotu null. - skutečným typovým parametrem může být také hodnotový typ a hodnota null je povolena pouze pro referenční typy. K tomuto účelu lze použít výraz default (angl default value expression). výraz_default: default ( typ ) Typ – jméno typu nebo formálního typového parametru generického typu.
  • 19. Implicitní hodnoty Pokud typ reprezentuje referenční typ, výsledkem výrazu je hodnota null. Pokud představuje hodnotový typ, výsledkem je implicitní hodnota pro daný hodnotový typ, což je: 0 pro číselné a výčtové typy, false pro typ bool, null pro nulovatelné typy, výsledek volání implicitního konstruktoru pro uživatelem definované struktury – nastavení implicitních hodnot pro všechny datové složky.
  • 20. Generická rozhraní deklarace generického rozhraní: modifikátorynep partialnep interface jméno seznam_typových_parametrů předeknep omezení_typových_parametrůnep { složky_rozhraní } ;nep Typové parametry a omezení typových parametrů mají stejný význam jako u deklarace generické třídy nebo struktury.
  • 21. Generická rozhraní – příklad Příklad demonstruje deklaraci generického rozhraní zásobníku a jeho implicitní implementaci v generické třídě GenZasobnik. https://goo.gl/hFjMZe
  • 22. Generická rozhraní Generické rozhraní samozřejmě může implementovat i negenerický typ. Např. class IntZasobnik : IZasobnik<int> { public void Vloz(int obj) { ... } public int Odeber() { ... } }
  • 23. Generická rozhraní Explicitně implementované složky generického rozhraní se kvalifikují konstruovaným generickým rozhraním, např. class ExplicitGenZasobnik<T> : IZasobnik<T> { // ... void IZasobnik<T>.Vloz(T obj) { ... } T IZasobnik<T>.Odeber() { ... } }
  • 24. Generické delegáty deklarace generického delegátu: modifikátornep delegate typ jméno seznam_typových_parametrů ( seznam_parametrůnep ) omezení_typových_parametrůnep ; Typové parametry a omezení typových parametrů mají stejný význam jako u deklarace generické třídy nebo struktury.
  • 25. Generické delegáty – příklad V příkladu je použita generická třída GenZasobnik z předchozího příkladu. Obsahuje navíc metodu Najdi, která hledá první výskyt prvku v zásobníku, pro který zadaný predikát vrací hodnotu true. Predikát je realizován generickým delegátem. Zásobník je v hlavní metodě použit pro uložení řetězců znaků načtených z klávesnice. Pomocí metody Najdi se hledá první výskyt řetězce, který začíná zadaným textem. Skutečným parametrem metody Najdi je výraz lambda. Příkaz volání metody Najdi by bylo možné také zapsat pomocí dvou příkazů např. s použitím anonymní metody: Predikat<string> predikat = delegate(string hodnota) { return hodnota.StartsWith(pocatek); }; string text = zasobnik.Najdi(predikat); https://goo.gl/b3OMXG
  • 26. Generické metody Generická metoda je metoda, jejíž deklarace zahrnuje seznam typových parametrů a případné jejich omezení. Její syntaxe deklarace je následující: deklarace generické metody: modifikátorynep typ jméno seznam_typových_parametrů ( seznam_parametrůnep ) omezení_typových_parametrůnep tělo Typové parametry a omezení typových parametrů mají stejný význam jako u deklarace generické třídy nebo struktury.
  • 27. Generické metody Generická metoda obsahuje za jménem metody v lomených závorkách formální typové parametry oddělené čárkou. Pro jednotlivé typové parametry může obsahovat omezení, které se uvádějí až za seznamem formálních parametrů metody. Formální typový parametr generické metody lze použít jako návratový typ, typ formálního parametru metody nebo v těle metody. Generická metoda může být deklarována v generické i negenerické třídě, struktuře nebo rozhraní. Je-li deklarována v rámci generického typu, mohou být v ní použity i typové parametry tohoto generického typu.
  • 28. Generické metody – příklad Třída Obecne obsahuje statickou generickou metodu Vymena, která vymění hodnoty dvou proměnných. https://goo.gl/tYajjI
  • 29. Generické metody Při přetěžování metod se do signatury metody nezahrnuje ani omezení typových parametrů ani jména typových parametrů. Rozhodují jen pravidla platná pro přetěžování negenerických metod, tj. počet, typ, pořadí a způsob předávání parametrů. Rozlišuje se ale stejně pojmenovaná generická a negenerická metoda. Např. nemohou být deklarovány dvě uvedené metody f. class A { void f<T>(T x) { } void f<U>(U x) { } // Chyba – existuje již f<T> }
  • 30. Generické metody Předefinovaná virtuální generická metoda nesmí obsahovat žádná omezení typových parametrů. Zdědí tato omezení z původní virtuální metody. Do instance delegátu lze přiřadit i odkaz na generickou metodu. Generická metoda se v takovém případě uvádí jejím jménem, za nímž se v lomených závorkách specifikují skutečné typové parametry. Typové parametry se mohou vynechat, potom se odvodí z parametrů delegátu, do kterého se generická metoda přiřazuje. https://goo.gl/0ia1wG
  • 31. Direktiva using Direktivu using pro deklaraci nového jména pro daný typ lze použít jen pro zavřený konstruovaný typ. Např. using SlovnikIS = Slovnik<int, string>; // OK using SlovnikX = Slovnik; // Chyba class Program { static void Main(string[] args) { SlovnikIS slovnik = new SlovnikIS(); slovnik.Pridej(1, "text"); // ... } }
  • 32. Omezení typových parametrů V deklaraci generického typu nebo generické metody lze specifikovat omezení pro typové parametry. Omezení je nutné specifikovat pro typový parametr, pokud je potřebné přistupovat k jeho složkám, např. volat jeho metodu. Pokud typový parametr nemá specifikována žádná omezení, lze s ním pracovat, jako by byl typu object.
  • 33. Omezení typových parametrů – syntaxespecifikace_omezení_typového_parametru specifikace_omezení_typového_parametru omezení_typových_parametrů specifikace_omezení_typového_parametru: where typový_parametr : omezení_typového_parametru omezení_typového_parametru: primární_omezení seznam_sekundárních_omezení konstruktorové_omezení seznam_omezení Seznam omezení – seznam maximálně tří druhů omezení oddělených čárkou v pořadí primární, sekundární a konstruktorové.
  • 34. Primární omezení Primární omezení pro typový parametr má následující syntaxi: typ_třídy class struct Typ_třídy – jméno typu třídy.
  • 35. Omezení – příklad Je dána třída entity Entita, mající vlastnost Id. Od ní je odvozena třída uživatele Uzivatel. Dále je deklarována generická třída EntitaSeznam zapouzdřující pole entit a obsahující metodu NajdiId, která hledá index entity v poli podle zadaného Id. Aby se mohlo v této metodě přistupovat k vlastnosti Id prvku pole, musí se pro typový parametr T specifikovat primární omezení určující, že T musí být typu Entita. Metoda Main demonstruje použití seznamu entit na seznamu uživatelů. https://goo.gl/Y2l05I
  • 36. Omezení – příklad – varianty Pokud by třída EntitaSeznam neobsahovala specifikaci omezení pro typový parametr, příkaz #1 by mohl být nahrazen příkazem if ((item as Entita).Id == id) return index; // OK Avšak následující příkaz by byl chybný: if (((Entita)item).Id == id) return index; // Chyba
  • 37. Sekundární omezení seznam_sekundárních_omezení: typ_rozhraní typový_parametr seznam_sekundárních_omezení , typ_rozhraní seznam_sekundárních_omezení , typový_parametr Typ_rozhraní – jméno rozhraní. Typový_parametr – jméno jiného formálního typového parametru.
  • 38. Sekundární omezení Seznam sekundárních omezení může obsahovat seznam rozhraní oddělených čárkou, které daný typový parametr musí implementovat nebo musí být přímo typu uvedeného rozhraní. Dále může obsahovat jméno jiného typového parametru, kterému má být daný typový parametr roven nebo musí být jeho potomkem. Např. následující generická třída A vyžaduje, aby typový parametr U byl stejného typu jako typový parametr T nebo byl jeho potomkem a zároveň, aby implementoval rozhraní ICloneable. class A<T, U> where U : T, ICloneable { }
  • 39. Sekundární omezení – příklad Předchozí přiklad je rozšířen o seřazení seznamu entit podle Id. Řazení provádí metoda Serad třídy EntitaSeznam, v níž se porovnávají dvě entity pomocí metody ComapareTo rozhraní IComparable. Toto rozhraní tudíž musí třída Entita implementovat a musí být specifikováno v omezení typového parametru třídy EntitaSeznam. https://goo.gl/g36Crj
  • 40. Sekundární omezení – příklad – varianty Pokud by třída EntitaSeznam neobsahovala specifikaci omezení IComparable, příkaz #2 by mohl být nahrazen jedním z následujících příkazů: if (((IComparable)pole[j]).CompareTo(pole[j + 1]) > 0) { if ((pole[j] as IComparable).CompareTo(pole[j + 1]) > 0) {
  • 41. Sekundární omezení – příklad – varianty Při použití rozhraní IComparable se provádí přetypování mezi typem object a skutečným typem prvku pole. Pokud by prvkem pole byl hodnotový typ, provádělo by se zabalení a vybalení, což snižuje výkon aplikace. Proto od verze .NET 2.0 je k dispozici i generické rozhraní IComparable<T> deklarované v prostoru jmen System následovně: public interface IComparable<T> { int CompareTo(T other) } https://goo.gl/f5poNk
  • 42. Konstruktorové omezení konstruktorové_omezení: new ( ) Konstruktorové omezení specifikuje, že typový parametr reprezentuje referenční typ, který má veřejný bezparametrický konstruktor. Jen pomocí tohoto konstruktoru lze v generickém typu nebo metodě vytvořit novou instanci typu reprezentovaného formálním typovým parametrem.
  • 43. Konstruktorové omezení Příklad: class A<T> where T : new() { T x; public A() { x = new T(); // #1 } }
  • 44. Konstruktorové omezení class B<T> where T : struct { T x; public B() { x = new T(); // #2 } }
  • 45. Doporučení pro pojmenování Formální typové parametry by měly být pojmenovány podle následujících pravidel: • Název typového parametru by měl začínat písmenem T. • Lze-li typový parametr nahradit libovolným typem, protože pro něj není specifikováno žádné omezení a zároveň se používá pouze jeden typový parametr, lze použít samotné písmeno T.
  • 46. Doporučení pro pojmenování •Je-li pro typový parametr specifikováno omezení, např. že musí implementovat nějaké rozhraní nebo musí být odvozen od dané třídy, nebo jestliže se používá více typových parametrů, měly by se použít popisné názvy. Např. pokud typ typového parametru musí implementovat rozhraní IComparable, měl by se jmenovat TComparable, nebo pokud typ typového parametru musí být odvozen od třídy Entita, měl by se jmenovat TEntita: class TridenySeznam<TComparable> where TComparable : IComparable class EntitaSeznam<TEntita> where TEntita : Entita class Dictionary<TKey,TValue> { }
  • 47. Q & A