+ All Categories
Home > Education > INPTP přednáška 03 2016

INPTP přednáška 03 2016

Date post: 12-Apr-2017
Category:
Upload: jan-hridel
View: 136 times
Download: 0 times
Share this document with a friend
47
Pokročilé techniky programování Přednáška 03 – Genericita
Transcript
Page 1: INPTP přednáška 03 2016

Pokročilé techniky programováníPřednáška 03 – Genericita

Page 2: INPTP přednáška 03 2016

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.

Page 3: INPTP přednáška 03 2016

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;

} }

Page 4: INPTP přednáška 03 2016

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();

Page 5: INPTP přednáška 03 2016

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 >

Page 6: INPTP přednáška 03 2016

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é.

Page 7: INPTP přednáška 03 2016

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>

Page 8: INPTP přednáška 03 2016

Generické třídy – použití gen. typu Nesvázaný generický typ

Konstruovaný generický typ

Page 9: INPTP přednáška 03 2016

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.

Page 10: INPTP přednáška 03 2016

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>>.

Page 11: INPTP přednáška 03 2016

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.

Page 12: INPTP přednáška 03 2016

Vnořené třídyTří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

} }

Page 13: INPTP přednáška 03 2016

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();

} }

Page 14: INPTP přednáška 03 2016

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.

Page 15: INPTP přednáška 03 2016

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

Page 16: INPTP přednáška 03 2016

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í.

Page 17: INPTP přednáška 03 2016

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í).

Page 18: INPTP přednáška 03 2016

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.

Page 19: INPTP přednáška 03 2016

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.

Page 20: INPTP přednáška 03 2016

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.

Page 21: INPTP přednáška 03 2016

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

Page 22: INPTP přednáška 03 2016

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() { ... } }

Page 23: INPTP přednáška 03 2016

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() { ... } }

Page 24: INPTP přednáška 03 2016

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.

Page 25: INPTP přednáška 03 2016

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

Page 26: INPTP přednáška 03 2016

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.

Page 27: INPTP přednáška 03 2016

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.

Page 28: INPTP přednáška 03 2016

Generické metody – příklad Třída Obecne obsahuje statickou generickou metodu Vymena, která vymění hodnoty dvou proměnných.

https://goo.gl/tYajjI

Page 29: INPTP přednáška 03 2016

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> }

Page 30: INPTP přednáška 03 2016

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

Page 31: INPTP přednáška 03 2016

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"); // ... } }

Page 32: INPTP přednáška 03 2016

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.

Page 33: INPTP přednáška 03 2016

Omezení typových parametrů – syntaxe

specifikace_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é.

Page 34: INPTP přednáška 03 2016

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.

Page 35: INPTP přednáška 03 2016

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

Page 36: INPTP přednáška 03 2016

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

Page 37: INPTP přednáška 03 2016

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.

Page 38: INPTP přednáška 03 2016

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 { }

Page 39: INPTP přednáška 03 2016

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

Page 40: INPTP přednáška 03 2016

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) {

Page 41: INPTP přednáška 03 2016

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

Page 42: INPTP přednáška 03 2016

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.

Page 43: INPTP přednáška 03 2016

Konstruktorové omezení Příklad:

class A<T> where T : new() { T x;

public A() { x = new T(); // #1 } }

Page 44: INPTP přednáška 03 2016

Konstruktorové omezení class B<T> where T : struct { T x; public B() { x = new T(); // #2 } }

Page 45: INPTP přednáška 03 2016

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.

Page 46: INPTP přednáška 03 2016

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> { }

Page 47: INPTP přednáška 03 2016

Q & A


Recommended