Rozhraní (interface) - připomíná abstraktní třídu, jejíž všechny členy jsou abstraktní - je to seznam (hlaviček) metod, vlastností a událostí - „třída implementuje rozhraní“ = třída definuje verze všech složek v tomto rozhraní deklarovaných - implementace rozhraní je příslib, co budou umět všechny instance této třídy („protokol“) - v jazyce C# není vícenásobná dědičnost, ale třída může implementovat více rozhraní (→ její instance jsou několika různých typů) - tělo rozhraní může být i prázdné – pak slouží pouze k „označení“ třídy, k přidělení dalšího typu Použití rozhraní: jako typ proměnné nebo parametru, do kterého lze dosadit instanci libovolné třídy, která toto rozhraní implementuje → jednotné zpracování nepříbuzných tříd metodami uvedenými v rozhraní Pavel Töpfer, 2016
Programování II - 5
1
Syntaxe definice rozhraní: - rozhraní se definuje podobně jako třída, místo class je interface - uvádějí se v něm pouze hlavičky funkcí – bez těl a bez modifikátorů přístupu - u vlastností se uvádí jedna nebo obě sekce ve tvaru set; resp. get;
Syntaxe implementace rozhraní: class Třída: Rozhraní { … } //Třída implementuje Rozhraní class Třída: Rozhraní1, Rozhraní2, Rozhraní3 { … } //Třída implementuje několik Rozhraní class Třída: Předek, Rozhraní1, Rozhraní2 { … } //Třída dědí od Předka (nelze dědit od více) //a navíc implementuje jedno nebo několik Rozhraní Pavel Töpfer, 2016
Programování II - 5
2
public class ZaklObjekt { //základní objekt } interface IZvetsovaci { void ZvetseniX(double faktor); void ZvetseniY(double faktor); } public class ZvetsObjekt : ZaklObjekt, IZvetsovaci { public void ZvetseniX(double faktor) { //objekt se zvětší v souřadnici X } public void ZvetseniY(double faktor) { //objekt se zvětší v souřadnici Y } } Pavel Töpfer, 2016
Programování II - 5
3
Obvyklá konvence pro názvy rozhraní: - název začíná písmenem I - následuje slovo udávající, jaký je objekt, jehož třída příslušné rozhraní implementuje v češtině „-telný“ „-ací“ apod., v angličtině zpravidla „-able“ např. IZvětšovací, IZvětšitelný, IZoomable, IScalable, …
Pavel Töpfer, 2016
Programování II - 5
4
Příklad rozhraní z knihovny .NET - rozhraní IComparable - obsahuje jedinou metodu CompareTo() pro porovnání dvou objektů - využívá se u polí a seznamů v metodě Sort() - např. třídy Int32 a String toto rozhraní implementují, takže pole čísel nebo pole stringů lze pomocí Array.Sort() setřídit - máme-li v programu pole objektů typu Pes a zavoláme na něj Sort(), dojde k běhové chybě, kdy třída Pes neimplementuje rozhraní IComparable (není jasné, podle čeho se mají psy řadit – zda abecedně podle jména, nebo třeba podle toho, kolik lidí pokousali) - řešení: když chceme psy v poli řadit pomocí Sort(), musí třída Pes implementovat IComparable a definovat metodu CompareTo() - definice metody CompareTo(): porovná instanci X, od které byla volána, s instancí Y předanou v parametru, výsledek vrací jako int: jakékoliv záporné číslo, když X < Y nula, když X = Y jakékoliv kladné číslo, když X > Y Pavel Töpfer, 2016
Programování II - 5
5
Příklad: řazení psů v poli (a výpis psů) class Program { static void Main(string[] args) { Pes[] psi = new Pes[] { new Pes("alík", 2), new Pes("Hugo", 27), new Pes("Tyrl", 10), new Pes("Sultán", 10), new Pes("punťa", 0) }; Array.Sort(psi); foreach (Pes p in psi) Console.WriteLine(p);
} } Pavel Töpfer, 2016
Programování II - 5
6
class Pes : IComparable { public string jmeno; public int KolikLidiPokousal; public Pes(string jmeno, int KolikLidiPokousal) { this.jmeno = jmeno; this.KolikLidiPokousal = KolikLidiPokousal; } //předefinování metody ToString(), //abychom mohli psy jednoduše vypisovat public override string ToString() { return jmeno + " " + KolikLidiPokousal; }
Pavel Töpfer, 2016
Programování II - 5
7
//porovnává psy podle počtu pokousaných lidí, //v případě shody abecedně podle jména public int CompareTo(object pes) { int vysledek = KolikLidiPokousal – ((Pes)pes).KolikLidiPokousal; if (vysledek == 0) return jmeno.CompareTo(((Pes)pes).jmeno); else return vysledek; } }
V metodě CompareTo() třídy Pes zde využíváme standardně nadefinovanou metodu CompareTo() třídy String.
Pavel Töpfer, 2016
Programování II - 5
8
Test příslušnosti instance do třídy nebo rozhraní - volající kód neví, zda je objekt správné třídy nebo zda podporuje určité rozhraní, proto si to chce před přetypováním nejdříve zkontrolovat - operátor is typu bool - pokud víme jistě, že je instance správné třídy, můžeme ji přetypovat: (typ) instance … typem může být třída nebo rozhraní
- pokusíme-li se provést nekorektní přetypování → výjimka InvalidCastException - bezpečné přetypování: operátor as – v případě neúspěchu nevyvolá výjimku, ale místo toho vrátí null (což můžeme následně otestovat)
Pavel Töpfer, 2016
Programování II - 5
9
public class ZaklObjekt { //základní objekt } interface Zvetsovaci { void ZvetseniX(double faktor); void ZvetseniY(double faktor); } public class ZvetsObjekt : ZaklObjekt, Zvetsovaci { public void ZvetseniX(double faktor) { //objekt se zvětší v souřadnici X } public void ZvetseniY(double faktor) { //objekt se zvětší v souřadnici Y } } Pavel Töpfer, 2016
Programování II - 5
10
ZaklObjekt ddd = new ZaklObjekt(); //ZaklObjekt ddd = new ZvetsObjekt(); if (ddd is Zvetsovaci) { //můžeme přetypovat do aaa Zvetsovaci aaa = (Zvetsovaci)ddd; aaa.ZvetseniX(1.4); aaa.ZvetseniY(2.0); } Zvetsovaci bbb = ddd as Zvetsovaci; if (bbb != null) { //máme přetypováno, dále pracujeme s bbb bbb.ZvetseniX(1.4); bbb.ZvetseniY(2.0); }
Pavel Töpfer, 2016
Programování II - 5
11
Struktura – struct - „zjednodušená třída“ - má podobný význam a použití jako pascalský záznam (record) - navíc může mít metody (jako třída) - je to hodnotový typ (na rozdíl od instance třídy se nemusí alokovat) - některá omezení oproti třídám (např. nemůže dědit, nepodporuje polymorfismus) - může mít i konstruktor (vlastní konstruktor musí inicializovat všechny datové složky, jinak má i implicitní bezparametrický konstruktor) - instance se ale nemusí vytvářet pomocí new + volání konstruktoru - konstruktor se sice volá s operátorem new, ale to zde nezpůsobí žádnou novou dynamickou alokaci paměti; konstruktor nevrací referenci na objekt, ale přímo hodnotu struktury (dočasně umístěnou na zásobníku), která se zkopíruje na požadované místo
Pavel Töpfer, 2016
Programování II - 5
12
Příklad struktury struct Bod { public int x, y; public Bod(int x, int y) { this.x = x; this.y = y; } } Bod a = new Bod(10, 10); Bod b = a; a.x = 20; Console.WriteLine(b.x);
Je-li Bod struktura, program vypíše 10 (proměnné a, b jsou nezávislé). Je-li Bod třída, program vypíše 20 (proměnné a, b jsou reference na tentýž objekt) Pavel Töpfer, 2016
Programování II - 5
13
Další příklady použití struktury - struktura System.DateTime pro uložení časového údaje (datum a čas), struktura System.TimeSpan pro uložení časového intervalu - pár klíč – hodnota uložený ve slovníku (KeyValuePair v kolekci Dictionary) - všechny hodnotové typy (jako int, bool, char) jsou ve skutečnosti struktury (např. mají metody)
Pavel Töpfer, 2016
Programování II - 5
14
Výčtový typ - hodnotový typ - skupina pojmenovaných konstant (představují celá čísla) enum jméno { seznam konstant } enum UdalostAuta {zacatekNakladky, nalozeno, uVykladky, zacatekVykladky, vylozeno, vBodeC, vBodeD, uNakladky}; - lze definovat hodnoty jednotlivých konstant enum stav {ok = 0, chyba = 1, katastrofa = 8}; - při použití musíme konstanty kvalifikovat jménem typu, např. stav.ok UdalostAuta.vylozeno - operátory: pouze ++ -- a bitové operátory
Pavel Töpfer, 2016
Programování II - 5
15
Prostor jmen – namespace - určuje rozsah platnosti identifikátorů - každá třída je definována v nějakém jmenném prostoru - více programů může patřit do stejného jmenného prostoru - v jednom program lze definovat více jmenných prostorů - jmenné prostory mohou být vnořovány do sebe - ve jmenném prostoru lze na nejvyšší úrovni definovat pouze prvky pěti základních druhů: class = třída struct = zjednodušená třída interface = rozhraní enum = enumerace, výčet hodnot delegate = delegát (prototyp volání metody)
Pavel Töpfer, 2016
Programování II - 5
16
- identifikátory tříd (a ostatních základních prvků) se kvalifikují jménem příslušného jmenného prostoru - místo toho lze na začátku programu jako zkratku použít using
(trochu analogie příkazu with v Pascalu) System.Console.WriteLine(); nebo
using System; Console.WriteLine();
Pavel Töpfer, 2016
Programování II - 5
17
POZOR - příkaz using nelze použít pro třídy: using System.Console; WriteLine(); je špatně!
Pomocí using lze ale vytvářet aliasy tříd: using = ; using con = System.Console; con.WriteLine();
Pavel Töpfer, 2016
Programování II - 5
18
Už rozumíme všemu, co nám Visual Studio připraví v nové konzolové aplikaci: using using using using
System; System.Collections.Generic; System.Linq; System.Text;
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { } } }
Pavel Töpfer, 2016
Programování II - 5
19
Kolekce = kontejnery, kontejnerové třídy
- instance fungují jako seznamy s libovolným počtem prvků - různé typy kolekcí se liší tím, jaký přístup k prvkům umožňují (např. seznam, zásobník, fronta, slovník, hešovací tabulka, …) - často definují indexer → k prvkům kolekce lze pak přistupovat také indexováním kolekcí podobně jako u polí - jsou definovány v prostoru jmen System.Collections - některé kolekce jsou generické – jsou definovány v prostoru jmen System.Collections.Generic Cyklus pro zpracování všech prvků v kolekci: příkaz foreach foreach (typ proměnná in kolekce) příkaz;
Pavel Töpfer, 2016
Programování II - 5
20
Přehled nejdůležitějších kolekcí ArrayList - seznam odkazů na instance třídy object (což je společný předek všech typů, tzn. lze tam vložit střídavě cokoliv) - pochází ze C# 1.0, dnes se už moc nepoužívá - nutno použít using System.Collections; List - generický seznam prvků zvoleného typu (v C# od verze 2.0 fakticky nahradila třídu ArrayList) - definována v prostoru jmen System.Collections.Generic LinkedList - generický seznam prvků zvoleného typu, podobně jako List - implementován spojovým seznamem, každý prvek alokován samostatně - umožňuje rychlé vkládání a vypouštění prvků Pavel Töpfer, 2016
Programování II - 5
21
Hashtable - vyhledávací struktura umožňující rychlé hledání prvku pole klíče (hešovací tabulka) - ukládá dvojice klíč – hodnota (dva objekty, oba libovolného typu) - klíčem lze kolekci indexovat (při vkládání i při vyhledávání) - dnes se už moc nepoužívá, nahrazena novější kolekcí Dictionary Dictionary - novější a efektivnější implementace hešovací tabulky - je to generická kolekce, typ1 určuje typ klíče, typ2 je typem hodnoty SortedList - generická kolekce s funkcemi velmi podobnými Dictionary - uspořádaný spojový seznam umožňující hledání klíče půlením intervalů - oproti Dictionary má menší paměťové nároky, ale vyhledávání je obvykle o něco pomalejší
Pavel Töpfer, 2016
Programování II - 5
22
BitArray - bitové pole – abstraktní typ pro práci s bitovými daty - konstruktor zkonvertuje zadaný int, pole typu int nebo pole typu bool - poskytuje uživateli pohodlné bitové operace Stack - abstraktní datový typ implementující funkčnost zásobníku Queue - abstraktní datový typ implementující funkčnost fronty
Pavel Töpfer, 2016
Programování II - 5
23
Kapacita kolekce Každá kolekce je interně implementována s nějakou implicitní počáteční kapacitou (= kolik se do ní vejde prvků). Při překročení této kapacity je realokována, což stojí čas a paměť (např. během postupného vkládání 100 prvků je obvykle realokována několikrát). Konkrétně základní kolekce List v současné implementaci začíná s kapacitou 4, při každé realokaci se její kapacita zdvojnásobí. Pokud dokážeme předem odhadnout skutečný počet prvků, můžeme nastavit kapacitu vytvářené kolekce v parametru jejího konstruktoru. Tím výpočet zpravidla urychlíme (a pokud ani tato kapacita nestačí, nic špatného se nestane).
Pavel Töpfer, 2016
Programování II - 5
24
Třída ArrayList ArrayList a = new ArrayList();
- prvky jsou objekty uložené a přístupné v poli od indexu 0 - neudává se velikost pole, po zaplnění je nahrazeno větším polem - může obsahovat vícekrát stejnou hodnotu, může obsahovat null - vlastnosti a metody: Add() – přidání prvku (na konec) a.Add(x); IndexOf() – vyhledání prvku, vrací nezáporné číslo = index, -1 když není if (a.IndexOf(x)==-1) { … } Sort() – seřazení prvků a.Sort(); (… prvky v kolekci musí být porovnatelné – stejného typu!) Count, Clear(), Contains(), Remove(), …
Pavel Töpfer, 2016
Programování II - 5
25
Příklad:
ArrayList AL = new ArrayList(); AL.Add("První"); AL.Add(222); AL.Add(null); AL.Add("Třetí"); Console.WriteLine(" Počet: {0}", AL.Count ); Console.WriteLine(" Prvni prvek: {0}", AL[0]);
Console.Write(" Všechny prvky: "); foreach (Object obj in AL) Console.Write(" {0}", obj); Console.WriteLine();
Pavel Töpfer, 2016
Programování II - 5
26
Třída List List cisla = new List(); cisla.Add(27); cisla.Add(89); cisla.Add(154); cisla.Add(207); cisla.Add(341); foreach (int cislo in cisla) { Console.Write(" {0}", cislo); } Console.WriteLine(); Console.WriteLine(cisla.Count);
Pavel Töpfer, 2016
Programování II - 5
27
class Komplex { public float Re, Im; public Komplex(float Re, float Im) { this.Re = Re; this.Im = Im; } public double AbsolutniHodnota() { return Math.Sqrt( Re*Re+Im*Im ); } } List body.Add( new body.Add( new body.Add( new body.Add( new
body = new List(); Komplex(1,0) ); Komplex(1,3) ); Komplex(2,0) ); Komplex(7,2) );
foreach (Komplex k in body) { Console.Write(" {0}", k.AbsolutniHodnota() ); } Pavel Töpfer, 2016
Programování II - 5
28
Generická třída List obsahuje řadu metod, například Add(), Contains(), Sort(), BinarySearch(), … List<string> sl = new List<string>(); sl.Add ("abcd"); sl.Add ("efgh"); if (sl.Contains( "efgh" )) Console.WriteLine("obsahuje" );
Pavel Töpfer, 2016
Programování II - 5
29
Kolekci lze indexovat a pracovat s uloženými hodnotami jako s prvky jednorozměrného pole – indexuje se vždy od 0. Metoda Add() přidává nový prvek na konec seznamu, lze ale také: - vkládat na zvolenou pozici Insert(kde, co) - vypouštět prvek ze zvolené pozice RemoveAt(kde) - vyhledat pozici prvního výskytu IndexOf(co)
List cisla = new List(); … Console.WriteLine(cisla[3]); //použití prvku cisla[3] = 555; //změna uložené hodnoty … cisla.Insert(5, 33333); //vložení, nikoli přepsání cisla.RemoveAt(5); //vypuštění, ostatní se posunou foreach (int c in cisla) Console.Write("{0}:", c); Console.WriteLine(); Pavel Töpfer, 2016
Programování II - 5
30
Hešování klíč hešovací funkce index v poli (hešovací tabulka) rozsah možných klíčů bývá výrazně větší než rozsah přípustných indexů – např. klíč je rodné číslo člověka, pro uložení několik set lidí stačí pole indexované 0..999
hešovací funkce nebývá prostá vznikají kolize (více záznamům je přidělen stejný index v poli) 1. Minimalizace počtu kolizí: dobrá rovnoměrně rozptylující hešovací funkce, dostatečný rozsah indexů vzhledem k počtu ukládaných záznamů (zaplněnost hešovací tabulky do 90%) 2. Řešení kolizí: oblast přetečení nebo sekundární transformace klíče
Pavel Töpfer, 2016
Programování II - 5
31
Kolekce Hashtable - forma hešovací tabulky v C# - narozdíl od teorie neřeší kolize, ukládá dvojice klíč-hodnota, přičemž klíč musí být jednoznačný - pokud bychom chtěli mít možnost přiřadit jednomu klíči více různých hodnot, museli bychom použít kolekci MultiMap - to je vlastně kolekce Dictionary, v níž hodnota je typu List (každému klíči je tak přiřazen seznam hodnot) - klíč i hodnota jsou typu object, tzn. do kolekce lze vložit dvojice s libovolnými typu klíčů i hodnot (dokonce ani v jedné kolekci nemusí mít všechny uložené prvky stejný typ klíče) → problémy s přetypováním při vyzvedávání hodnot, možnost chyb - v současné době se proto místo kolekce Hashtable raději používá generická kolekce Dictionary, kde se provádí typová kontrola při kompilaci a navíc je práce s ní rychlejší Pavel Töpfer, 2016
Programování II - 5
32
Příklad: klíčem je string, hodnotou je int představující počet výskytů klíče using System.Collections; static Hashtable ht = new Hashtable(); static void Pridej(Hashtable ht, string s) { if (ht.Contains(s)) //test zda je klíč v tabulce { int x = (int)ht[s]; //hodnota odpovídající klíči ht[s] = x + 1; //hodnotu můžeme změnit } else ht.Add(s, 1); //vložení nové dvojice } // klíč - hodnota
Pavel Töpfer, 2016
Programování II - 5
33
static void Main(string[] args) { Pridej(ht, "prvni"); Pridej(ht, "druhe"); Pridej(ht, "treti"); Pridej(ht, "prvni"); Pridej(ht, "prvni"); foreach (System.Collections.DictionaryEntry de in ht) { Console.WriteLine("\t{0}:\t{1}", de.Key, de.Value); } }
Pavel Töpfer, 2016
Programování II - 5
34
Kolekce Dictionary - modernější, rychlejší, generická verze kolekce HashTable - opět ukládá dvojice klíč-hodnota, přičemž klíč je jednoznačný - typ klíče a hodnoty jsou parametrem generiky, tzn. do jedné kolekce se vkládají výhradně dvojice téhož typu → odpadají problémy s přetypováním - uložené dvojice klíč-hodnota jsou generického typu KeyValuePair (u Hashtable byly typu DictionaryEntry, což byla dvojice objektů)
Pavel Töpfer, 2016
Programování II - 5
35
Stejný příklad: klíčem je string, hodnotou je int představující počet výskytů klíče using System.Collections.Generic; static Dictionary<string, int> dict = new Dictionary<string, int>();
static void Pridej(Dictionary<string, int> dict, string s) { if (dict.ContainsKey(s)) //test zda tam klíč je { int x = dict[s]; //hodnota odpovídající klíči dict[s] = x + 1; //hodnotu můžeme změnit } else dict.Add(s, 1); //vložení nové dvojice } // klíč - hodnota
Pavel Töpfer, 2016
Programování II - 5
36
static void Main(string[] args) { Pridej(dict, "prvni"); Pridej(dict, "druhe"); Pridej(dict, "treti"); Pridej(dict, "prvni"); Pridej(dict, "prvni"); foreach (KeyValuePair<string, int> pair in dict) { Console.WriteLine("\t{0}:\t{1}", pair.Key, pair.Value); } }
Pavel Töpfer, 2016
Programování II - 5
37