kst/icshp - 5. a 6. přednáška

267
přednáška 05-06 a samostudium Fakulta elektrotechniky a informatiky Univerzita Pardubice 2015/2016

Upload: jan-hridel

Post on 22-Jan-2018

170 views

Category:

Education


0 download

TRANSCRIPT

přednáška 05-06 a samostudium

Fakulta elektrotechniky a informatiky Univerzita Pardubice 2015/2016

@h0nza

Raději už na začátku položte o otázku víc… Vás to nic nestojí a ušetříte si spoustu práce.

Vnořené třídy ◦ Stejně jako v C++ lze deklarovat uvnitř třídy

(struktury) vnořenou třídu (strukturu), která se stane její složkou a vztahují se na ní přístupová práva stejně jako na další složky.

◦ Rozdíly oproti C++ V C++ neplatí žádné výjimky při přístupu z vnější třídy ke

složkám vnitřní a naopak. V C# mohou složky vnitřní třídy přistupovat ke všem

složkám vnější třídy. V C# mohou složky vnější třídy přistupovat k veřejným

nebo interním složkám vnitřní třídy.

Vnořené třídy ◦ Doporučení Nedeklarovat vnitřní veřejné typy. Vnořené typy by

měly být používány jen vnějšími typy. ◦ Příklad

class A { private int x; public class B { private int y; public void f() { A a = new A(); a.x = 15; // OK } } public void g() { B b = new B(); // b.y = 25; // Chyba } }

Vytvoření instance vnořené třídy B mimo tělo vnější třídy lze provést příkazem A.B b = new A.B();

Dědičnost ◦ V C# pouze jednoduchá dědičnost. ◦ Pokud není uveden předek třídy, doplní překladač

jako předka třídu object. ◦ Potomek může předka zastoupit ◦ V C# lze odvozovat pouze od třídy (ne od struktury)

a dědit může opět jen třída (ne struktura). ◦ Syntaxe :jméno Jméno – identifikátor bázové třídy (možno i s uvedením

jmenného prostoru)

Dědičnost ◦ V C# je veškeré dědění veřejné. Ve specifikaci

předka nelze uvést přístupová práva ani modifikátor virtual. ◦ Potomek dědí všechny složky předka mimo

konstruktorů a destruktorů. Některé složky (soukromé a je-li potomek deklarován v jiném sestavení, tak i interní) však nemusí být v potomkovi přístupné.

Dědičnost ◦ Zastíněné složky Žádnou zděděnou složku nelze v potomkovi odstranit. Zděděné složky lze zastínit použitím stejného

identifikátoru (u metod včetně signatury) a užitím modifikátoru new.

Zastínit lze i virtuální metody, vlastnosti a indexery. Možnost přístupu k zastíněným rodičovským složkám

zůstává pomocí klíčového slova base.

Dědičnost ◦ Příklad: https://github.com/janhridel/icshp/blob/master/Prednasky/04/01_dedicnost.cs

class A { public void f() { Console.WriteLine("Výpis z metody A.f()"); } public void g() { Console.WriteLine("Výpis z metody A.g()"); } } class B : A { public new void f() // zastiňující metoda { base.f(); // volá se metoda f() předka Console.WriteLine("Výpis z metody B.f()"); } public void g(int i) { Console.WriteLine("Výpis z metody B.g(int i) => {0}",i); } // OK – new se neuvádí - v A je jiná signatura! } class Program { static void Main(string[] args) { B b = new B(); b.f(); // b.base.f(); // Chyba ((A)b).f(); // OK - volá se metoda f třídy A b.g(); // #1 - v C# OK, v C++ chyba b.g(10); // #2 – OK v C# i C++ Console.ReadLine(); } }

Dědičnost ◦ Při rozhodování, zda volat metodu potomka nebo

původní zastíněnou metodu předka se v C# uplatňují přístupová práva.

C#: „co není přístupné, o to nevím, to neberu v úvahu“

C++: „změna přístupových práv nesmí změnit chování programu“

Dědičnost ◦ Příklad

class A { public void f() { Console.WriteLine("metoda f() z A"); } } class B : A { protected new void f() { Console.WriteLine("metoda f() z B"); } } class Program { static void Main(string[] args) { B b = new B(); b.f(); // #1 – V C# se volá metoda f třídy A, v C++ chyba Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Příkaz #1 způsobí zavolání metody f třídy A, protože metoda f třídy B není přístupná. V C++ by tento příkaz byl chybný – překladač by oznámil, že metoda f třídy B není přístupná. Změna přístupových práv v jazyce C# může vést k poměrně zásadní změně v chování programu – mohou se volat jiné metody.

Dědičnost ◦ Zděděné a vlastní metody Byť se metody s různou signaturou v C# nezastiňují,

mají různá postavení Příklad

class A { public void f(int i) { Console.WriteLine("A.f(int i)"); } } class B : A { public void f(long i) { Console.WriteLine("B.f(long i)"); } } class Program { static void Main(string[] args) { B b = new B(); int i = 10; b.f(i); // #1 - volá f(long i) Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_zdedene_vlastni_metody Jak v C#, tak i v C++ příkaz #1 zavolá metodu f(long i) třídy B, protože typ int lze implicitně zkonvertovat na typ long.

Dědičnost ◦ Konstruktor předka

Konstruktor odvozené třídy vždy nejprve volá konstruktor předka.

Není-li uveden konkrétní konstruktor, volá se automaticky konstruktor bez parametrů.

Volání konstruktora předka se volá pomocí klíčového slova base.

class A { int x; public A(int x) { this.x = x; } } class B : A { int y; public B(int x, int y) : base(x) { this.y = y; } }

Dědičnost ◦ Polymorfismus

Polymorfní chování objektů lze implementovat v C# pomocí dědění nebo pomocí rozhraní.

V C# se polymorfně chovají pouze složky (metody, vlastnosti, události a indexery), které jsou deklarovány jako virtuální.

Virtuální složka se deklaruje v předkovi s modifikátorem virtual. V potomkovi se virtuální předefinovaná složka modifikuje pomocí override.

A musí mít stejná přístupová práva. Statické složky

nemohou být virtuální

Dědičnost ◦ Polymorfismus – příklad:

https://github.com/janhridel/icshp/blob/master/Prednasky/04/02_dedicnost_polymorfismus.cs

class Vlak { public void VypisNevirtualni() { Console.WriteLine("Vlak"); } public virtual void VypisVirtualni() { Console.WriteLine("Vlak"); } } class NakladniVlak : Vlak { public new void VypisNevirtualni() { Console.WriteLine("Nakladní vlak"); } public override void VypisVirtualni() { Console.WriteLine("Nakladní vlak"); } } class Program { static void Main(string[] args) { Vlak vlak1 = new Vlak(); Vlak vlak2 = new NakladniVlak(); vlak1.VypisNevirtualni(); vlak2.VypisNevirtualni(); vlak1.VypisVirtualni(); vlak2.VypisVirtualni(); Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_virtualni V uvedeném příkladu má bázová třída Vlak nevirtuální metodu VypisNevirtualni, která je v potomkovi NakladniVlak zastíněna. Pokud se tato metoda zavolá pro referenci typu Vlak, vždy se zavolá metoda třídy Vlak, ať se tato reference odkazuje na instanci třídy Vlak nebo třídy NakladniVlak. Třída Vlak dále obsahuje virtuální metodu VypisVirtualni(), která je v třídě NakladniVlak předefinována. Pokud se tato metoda zavolá pro referenci typu Vlak¸ která se odkazuje na instanci třídy NakladniVlak, zavolá se její předefinovaná verze v třídě NakladniVlak.

Dědičnost ◦ Polymorfismus Nelze deklarovat složku (metodu, vlastnost, událost

nebo indexer) s modifikátory new override. Lze však deklarovat složku s modifikátory new virtual, která znamená přerušení hierarchie virtuálních metod a zavedení nové virtuální metody, která zastiňuje virtuální metodu předka, s níž má společné pouze jméno.

Destruktory ◦ Je automaticky virtuální (destruktor je

předefinovaná virtuální metoda Finalize() třídy object). ◦ Nelze jej deklarovat s modifikátorem virtual. ◦ Při ukončení instance se nejprve volá destruktor

potomka, a potom automaticky destruktor předka – programátor volání předka nespecifikuje.

Destruktory ◦ Programátor

◦ Překladač přeloží

class A { ~A() { // příkazy destruktoru třídy A } }

class A { protected override void Finalize() { try { // příkazy destruktoru třídy A

} finally { base.Finalize(); } } }

Abstraktní třídy a metody ◦ Taková třída, pro kterou nelze vytvořit instanci. ◦ Může, ale nemusí obsahovat abstraktní složky. ◦ Obsahuje-li třída abstraktní metodu, musí být

deklarována jako abstraktní třída – pomocí modifikátoru abstract ◦ Abstraktní metoda je virtuální metoda bez těla. deklaruje se jako abstract, ne jako virtual místo těla má středník v potomkovi musí být předefinována (pokud není

odvozená třída též deklarována jako abstraktní) u abstraktních vlastností mají části get a set místo těla

středník

Abstraktní třídy a metody ◦ Příklad: https://github.com/janhridel/icshp/blob/master/Prednasky/05/01_abstract.cs

abstract class A { public abstract int X { get; set; } public abstract void f(); } abstract class B : A // musí být abstraktní { private int x; public override int X { get { return x; } set { x = value; } } public void g() { Console.WriteLine("Metoda g třídy B"); } } class C : B { public override void f() { Console.WriteLine("Metoda f třídy C"); } } class Program { static void Main(string[] args) { A a = new C(); a.X = 10; // zavolá přístupovou metodu vlastnosti X třídy B a.f(); // zavolá metodu f() třídy C Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_abstract Vypíše: Metoda f třídy C

Zapečetěné třídy a složky ◦ Třída s modifikátorem sealed ◦ Nelze od ní odvodit potomka ◦ Modifikátor sealed lze použít i v deklaraci virtuální

složky (metody, vlastnosti, události nebo indexeru)

◦ Zapečetit lze pouze složku s modifikátorem override, nikoli složku s modifikátorem virtual nebo nevirtuální složku.

◦ Abstraktní třídy a abstraktní složky nemohou být zapečetěné.

◦ Zapečetěné jsou např. třídy string a StringBuilder.

Takto zapečetěnou složku nelze v potomkovi předefinovat.

Statické třídy ◦ Může obsahovat jen statické složky ◦ Nelze vytvořit instanci ◦ Nemůže být předkem jiné třídy ◦ Deklaruje se s modifikátorem static

Statické třídy – příklad https://github.com/janhridel/icshp/blob/master/Prednasky/05/02_static.cs

static class A { static private int x; // void g() { } // Chyba - třída je statická static public int X { get { return x; } set { x = value; } } public static void f() { Console.WriteLine("x = " + x); } } class Program { static void Main(string[] args) { // A a = new A(); // Chyba - nelze vytvořit instanci statické třídy A.X = 10; A.f(); Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_static Výsledek: X = 10

Částečné třídy ◦ Třídy lze rozdělit do několika zdrojových souborů ◦ Uvádí se s modifikátorem partial ◦ Užití Ve VS u formulářových aplikacích je generovaný kód v

samostatném souboru Více programátorů pracujících na jedné velké třídě

(každý má svůj soubor) ◦ Složky deklarované v jedné části jsou dostupné i v

ostatních částech ◦ Specifikace předka může být v jedné nebo více

částech

Částečné třídy ◦ Různé části mohou implementovat různá rozhraní ◦ V různých částech mohou být různé atributy ◦ Všechny části musí mít stejná přístupová práva nebo mohou být vynechány, pokud jsou části ve

stejném sestavení ◦ Je-li jedna část třídy deklarována jako abstraktní či

zapečetěná, je celá třída abstraktní či zapečetěná ◦ Vnořené typy mohou být partial, i když vnější typ

není

Předvádějící
Poznámky prezentace
Třída tak implementuje veškerá uvedená rozhraní. Třída má pak všechny atributy

Částečné třídy ◦ Příklad

// Soubor B1.cs [Atribut1] partial class B : A { public void f(){} // ... } // Soubor B2.cs [Atribut1] [Atribut2] public partial class B // nemusí obsahovat specifikaci předka A { public void g(){} // ... }

Předvádějící
Poznámky prezentace
Při překladu projektu, jehož součástí jsou zdrojové soubory B1.cs i B2.cs, bude vytvořena jediná třída B, která bude veřejná, jejím předkem bude třída A, bude mít atributy Atribut1 a Atribut2 a bude obsahovat metody f() a g().

Interfaces - nemají v C++ bezprostřední analogii.

Obsahují seznam signatur metod, indexerů, vlastností a událostí bez těla.

Třída nebo struktura může rozhraní implementovat, tj. zavázat se, že implementuje jeho složky.

V C++ si lze rozhraní přibližně představit jako abstraktní třídu, která obsahuje pouze čisté virtuální metody.

Deklarace – syntaxe: ◦ modifikátorynep partialnep interface jméno předeknep { složky_rozhraní } ;nep Modifikátory – specifikují přístupová práva. U rozhraní, deklarovaného uvnitř třídy

lze použít modifikátor new Jméno – identifikátor deklarovaného rozhraní. Podle konvence pojmenování má

začínat velkým písmenem I, za nímž následuje vlastní jméno rozhraní s velkým počátečním písmenem, např. IEnumerable.

Seznam_rozhraní – seznam rozhraní oddělených čárkou, která představují předky deklarovaného rozhraní. Deklarované rozhraní obsahuje všechny složky svých předků .

Složky_rozhraní – seznam metod, indexerů, vlastností a událostí. Místo těla metody, přístupové metody vlastnosti a indexeru se uvádí středník. Jde vlastně o deklaraci odpovídající deklaraci abstraktních metod, indexerů a vlastností. V deklaraci složek nelze použít jakékoli modifikátory, všechny složky jsou v rozhraní veřejné.

◦ Rozhraní nemůže obsahovat deklaraci konstruktoru nebo destruktoru ani datových složek. Nelze vytvořit instanci rozhraní.

Implementace ◦ Pokud třída nebo struktura implementuje nějaká

rozhraní, uvádí se za jménem třídy nebo struktury v jejich deklaraci. ◦ Syntaxe :jméno_předka :seznam_rozhraní :jméno_předka, seznam_rozhraní

Předvádějící
Poznámky prezentace
Jméno_předka – identifikátor bázové třídy. Seznam_rozhraní – seznam rozhraní oddělených čárkou, které třída nebo struktura implementuje.

Implementace ◦ Třída může být potomkem jiné třídy nebo

implementovat jedno nebo více rozhraní nebo obojí. Nejprve se uvádí jméno předka a potom seznam

implementovaných rozhraní. ◦ Struktura nemůže mít předka, ale může

implementovat jedno nebo více rozhraní. ◦ Třída implementující rozhraní musí obsahovat

deklaraci všech jeho složek jako public. Tyto složky nejsou však automaticky virtuální nebo abstraktní, modifikátory virtual a abstract je potřeba doplnit.

Rozhraní jako typ ◦ Instance třídy implementující rozhraní je také

instancí tohoto rozhraní a lze s ní zacházet prostřednictvím referencí na toto rozhraní.

Předvádějící
Poznámky prezentace
Jestliže třída T implementuje rozhraní R, je každá instance třídy T také instancí rozhraní R a lze s ní zacházet prostřednictvím referencí na R. Jedná se o analogii v případě dědičnosti, kdy potomek může zastoupit předka.

Příklad: https://github.com/janhridel/icshp/blob/master/Prednasky/05/03_interface.cs

interface IVypisovací { void Vypis(); } interface IBod : IVypisovací { int X { get; set; } int Y { get; set; } event EventHandler ZmenaSouradnic; }

1

Předvádějící
Poznámky prezentace
Pr_rozhrani V příkladu je deklarováno rozhraní IVypisovaci, které implementuje třída Sestava a je předkem rozhraní IBod, které implementuje třída Bod. Metoda Vypis v třídě Sestava a Bod není virtuální. Metoda ProvedVypis pracuje s instancemi dvou naprosto rozdílných tříd, jejichž společným znakem je pouze to, že implementují rozhraní IVypisovaci.

Příklad

class Bod : IBod { int x, y; public int X { get { return x; } set { if (x != value) { x = value; OnZmenaSouradnic(EventArgs.Empty); } } } public int Y { // analogie vlastnosti X } public event EventHandler ZmenaSouradnic; public void Vypis() { Console.WriteLine("x = {0}, y = {1}", x, y); } protected virtual void OnZmenaSouradnic(EventArgs e) { EventHandler handler = ZmenaSouradnic; if (handler != null) handler(this, e); } } // konec Bod

2

Předvádějící
Poznámky prezentace
Pr_rozhrani V příkladu je deklarováno rozhraní IVypisovaci, které implementuje třída Sestava a je předkem rozhraní IBod, které implementuje třída Bod. Metoda Vypis v třídě Sestava a Bod není virtuální. Metoda ProvedVypis pracuje s instancemi dvou naprosto rozdílných tříd, jejichž společným znakem je pouze to, že implementují rozhraní IVypisovaci.

Příklad

class Sestava : Ivypisovací { public void Vypis() { Console.WriteLine("Výpis údajů sestavy"); } }

3

Předvádějící
Poznámky prezentace
Pr_rozhrani V příkladu je deklarováno rozhraní IVypisovaci, které implementuje třída Sestava a je předkem rozhraní IBod, které implementuje třída Bod. Metoda Vypis v třídě Sestava a Bod není virtuální. Metoda ProvedVypis pracuje s instancemi dvou naprosto rozdílných tříd, jejichž společným znakem je pouze to, že implementují rozhraní IVypisovaci.

Příklad

class Program { static void ProvedVypis(IVypisovací iv) { iv.Vypis(); } static double VzdalenostBodu(IBod bodA, IBod bodB) { return Math.Sqrt(Math.Pow(bodA.X - bodB.X, 2) + Math.Pow(bodA.Y - bodB.Y, 2)); } static void Main(string[] args) { Bod bodA = new Bod(); bodA.X = 10; bodA.Y = 20; Bod bodB = new Bod(); bodB.X = 30; bodB.Y = 40; Sestava sestava = new Sestava(); ProvedVypis(sestava); ProvedVypis(bodA); ProvedVypis(bodB); Console.WriteLine("Vzdálenost bodů je {0}", VzdalenostBodu(bodA, bodB)); Console.ReadKey(); } }

4

Předvádějící
Poznámky prezentace
Pr_rozhrani V příkladu je deklarováno rozhraní IVypisovaci, které implementuje třída Sestava a je předkem rozhraní IBod, které implementuje třída Bod. Metoda Vypis v třídě Sestava a Bod není virtuální. Metoda ProvedVypis pracuje s instancemi dvou naprosto rozdílných tříd, jejichž společným znakem je pouze to, že implementují rozhraní IVypisovaci.
Předvádějící
Poznámky prezentace
Pr_rozhrani V příkladu je deklarováno rozhraní IVypisovaci, které implementuje třída Sestava a je předkem rozhraní IBod, které implementuje třída Bod. Metoda Vypis v třídě Sestava a Bod není virtuální. Metoda ProvedVypis pracuje s instancemi dvou naprosto rozdílných tříd, jejichž společným znakem je pouze to, že implementují rozhraní IVypisovaci.

Explicitní implementace ◦ Nastane-li situace, že složka třídy a složka touto třídou

implementovaného rozhraní mají stejný identifikátor, lze složku rozhraní implementovat explicitně.

◦ Explicitně implementovaná složka rozhraní je v těle třídy kvalifikována jménem rozhraní. Taková složka je automaticky soukromá (nelze modifikovat

přístupová práva) a nemůže být abstraktní ani virtuální. Pokud se k této složce přistoupí prostřednictvím instance

třídy, použije se stejně pojmenovaná složka třídy. Pokud se k ní přistoupí prostřednictvím reference na rozhraní, použije se složka rozhraní.

Příklad: https://github.com/janhridel/icshp/blob/master/Prednasky/05/04_interface_explicitni_implementace.cs

interface IVypisovací { void Vypis(); } class A : IVypisovací { public void Vypis() { Console.WriteLine("Výpis údajů třídy A"); } }

1

Předvádějící
Poznámky prezentace
Pr_rozhrani_explicitni_implementace

Příklad

class B : A, IVypisovací { void IVypisovací.Vypis() // explicitní implementace { Console.WriteLine("Výpis údajů třídy B"); } } class Program { static void Main(string[] args) { B b = new B(); b.Vypis(); // metoda A.Vypis ((IVypisovací)b).Vypis(); // metoda B.Vypis Console.ReadKey(); } }

2

Předvádějící
Poznámky prezentace
Třída B obsahuje dvě metody Vypis. Jednu zděděnou z třídy A a druhou explicitně implementovanou z rozhraní IVypisovací. Voláním metody Vypis prostřednictvím instance třídy, se zavolá metoda Vypis třídy A. Má-li se zavolat explicitně implementovaná metoda Vypis, musí se volat pro referenci na rozhraní. Výpis programu bude následující: Výpis údajů třídy A Výpis údajů třídy B

Explicitní implementace ◦ Složku rozhraní má také smysl explicitně

implementovat v případě, kdy je žádoucí, aby se k ní přistupovalo pouze pomocí reference na rozhraní a ne pomocí reference na instanci třídy. ◦ Např. v rozhraní je deklarována metoda Pridej, ale

v třídě chceme deklarovat metodu PridejNaKonec a PridejNaZacatek. Rozhraní proto implementujeme explicitně a z metody Pridej voláme metodu např. PridejNaKonec.

Přetypování ◦ Pokud daná třída implementuje rozhraní, lze

instanci této třídy přetypovat na referenci na rozhraní (může proběhnout implicitně).

//ad předchozí příklad Ivypisovaci iv = b; //OK

object o; o = b; IVypisovaci iv = o; // Chyba – musí se přetypovat

reference o se musí přetypovat na požadované rozhraní pomocí operátoru (typ) nebo as IVypisovaci iv = (IVypisovaci)o; // OK

Přetypování ◦ Pomocí operátoru is lze nejprve otestovat, zda je

přetypování vůbec možné. ◦ Lze také přetypovat referenci na rozhraní IA na

referenci na rozhraní IB, pokud jde o třídu implementující obě tato rozhraní. Pokud je IB odvozeno od IA, implicitně se přetypuje

reference na IB na referenci na IA.

if (o is IVypisovací) iv = (IVypisovací)o; else iv = null;

Předefinování metod rozhraní ◦ Pokud předek třídy implementuje metodu rozhraní,

může tuto potomek předefinovat nebo zastínit. ◦ Virtuální metody Pokud je metoda rozhraní ve třídě deklarovaná jako

virtuální, lze ji v potomkovi předefinovat pomocí override – chování je pak stejné jako u klasických virtuálních metod.

Předefinování metod rozhraní ◦ Virtuální metody – příklad:

https://github.com/janhridel/icshp/blob/master/Prednasky/05/05_interface_virtualni_metody.cs

interface IVypisovací { void Vypis(); } class A : IVypisovací { public virtual void Vypis() { Console.WriteLine("Výpis údajů třídy A"); } } class B : A { public override void Vypis() { Console.WriteLine("Výpis údajů třídy B"); } }

1

Předvádějící
Poznámky prezentace
Pr_rozhrani_override_virtual

Předefinování metod rozhraní ◦ Virtuální metody - příklad

class Program { static void Main(string[] args) { A a = new A(); A b = new B(); IVypisovací ia = a; IVypisovací ib = b; a.Vypis(); b.Vypis(); ia.Vypis(); ib.Vypis(); Console.ReadKey(); } }

2

Předvádějící
Poznámky prezentace
Pr_rozhrani_override_virtual �Výpis údajů třídy A Výpis údajů třídy B Výpis údajů třídy A Výpis údajů třídy B

Předefinování metod rozhraní ◦ Nevirtuální metody V případě implementace metody Vypis v podobě

nevirtuální metody se pro referenci na rozhraní volá metoda třídy, která jako první v dědické hierarchii tříd implementuje dané rozhraní.

Příklad – viz předchozí + změna u tříd https://github.com/janhridel/icshp/blob/master/Prednasky/05/06_interface_nevirtualni_metody.cs

class A : IVypisovací { public void Vypis() { Console.WriteLine("Výpis údajů třídy A"); }} class B : A { public new void Vypis() { Console.WriteLine("Výpis údajů třídy B"); } }

Předvádějící
Poznámky prezentace
Pr_rozhrani_override_nevirtual Program zavolá metodu Vypis třídy A ve všech uvedených případech. Výpis programu bude následující: Výpis údajů třídy A Výpis údajů třídy A Výpis údajů třídy A Výpis údajů třídy A Metoda Vypis třídy B by se zavolala pouze pro proměnnou typu B: B b2 = new B(); b2.Vypis(); // metoda B.Vypis()

Předefinování metod rozhraní ◦ Nevirtuální metody Pokud je žádoucí, aby se prostřednictvím reference na

rozhraní volala nevirtuální zastiňující metoda potomka, musí se znovu implementovat rozhraní v potomkovi (implicitně nebo explicitně).

Příklad – viz předchozí + úprava https://github.com/janhridel/icshp/blob/master/Prednasky/05/07_interface_nevirtualni_m_potomek.cs

class A : IVypisovací { public void Vypis() { Console.WriteLine("Výpis údajů třídy A"); } } class B : A, IVypisovací { public new void Vypis() { Console.WriteLine("Výpis údajů třídy B"); } }

Předvádějící
Poznámky prezentace
Pr_rozhrani_override_potomek Výpis údajů třídy A Výpis údajů třídy A Výpis údajů třídy A Výpis údajů třídy B

Předdefinovaná rozhraní ◦ Uvedená rozhraní jsou součástí prostoru jmen System. ◦ IClonable

Slouží pro klonování instancí tříd nebo struktur.

Metoda Clone může zavolat chráněnou metodu MemberwiseClone třídy object, která provede mělkou kopii, nebo může definovat vlastní způsob klonování, který se postará o hloubkovou kopii.

public interface ICloneable { object Clone(); }

Předvádějící
Poznámky prezentace

Předdefinovaná rozhraní ◦ IClonable

class Bod : ICloneable { int x, y; public int X { get { return x; } set { x = value; } } public Bod(int x, int y) { this.x = x; this.y = y; } public virtual object Clone() { return MemberwiseClone(); } public override string ToString() { return "(" + x + ", " + y + ")"; } // ... }

1

Předvádějící
Poznámky prezentace
Pr_iclonable

Předdefinovaná rozhraní ◦ IClonable

class Usek : ICloneable { Bod pocatek, konec; int delka; public Bod Pocatek { get { return pocatek; } set { pocatek = value; } } public Usek(int x1, int y1, int x2, int y2, int delka) { pocatek = new Bod(x1, y1); konec = new Bod(x2, y2); this.delka = delka; } public virtual object Clone() { Usek t = (Usek)MemberwiseClone(); t.pocatek = (Bod)pocatek.Clone(); t.konec = (Bod)konec.Clone(); return t; } public override string ToString() { return "Počátek: " + pocatek + ", konec: " + konec + ", délka: " + delka; } // ... }

2

Předvádějící
Poznámky prezentace
Pr_iclonable

Předdefinovaná rozhraní ◦ IClonable

class Program { static void Main(string[] args) { Usek u1 = new Usek(1, 2, 3, 4, 100); Usek u2 = (Usek)u1.Clone(); u2.Pocatek.X = 10; Console.WriteLine(u1); Console.WriteLine(u2); Console.ReadKey(); } }

3

Předvádějící
Poznámky prezentace
Pr_iclonable

Předdefinovaná rozhraní ◦ IClonable doplnění příkladu Instanci třídy Bod lze kopírovat po složkách, proto je v

metodě Clone volána pouze metoda MemberwiseClone. V třídě Usek by metoda MemberwiseClone provedla pouze kopii referencí na instance bodů, nikoli kopii jejich složek. Proto se nejprve zavolá metoda MemberwiseClone, která mj. zkopíruje datovou složku delka a potom se volá metoda Clone pro oba body. Metody Clone jsou deklarovány jako virtuální, aby byla zajištěna jejich správná funkčnost i pro případné odvozené třídy.

Předvádějící
Poznámky prezentace
Pr_iclonable

Předdefinovaná rozhraní ◦ IClonable doplnění příkladu Instanci třídy Bod lze kopírovat po složkách, proto je v

metodě Clone volána pouze metoda MemberwiseClone. V třídě Usek by metoda MemberwiseClone provedla pouze kopii referencí na instance bodů, nikoli kopii jejich složek. Proto se nejprve zavolá metoda MemberwiseClone, která mj. zkopíruje datovou složku delka a potom se volá metoda Clone pro oba body. Metody Clone jsou deklarovány jako virtuální, aby byla zajištěna jejich správná funkčnost i pro případné odvozené třídy.

Předvádějící
Poznámky prezentace
Pr_iclonable

Předdefinovaná rozhraní ◦ IComparable

Rozhraní implementují typy, jejichž instance lze nějakým způsobem seřadit. Metoda CompareTo má vracet: zápornou hodnotu, je-li instance this menší než instance obj, nulu, je-li instance this rovna instanci obj, kladnou hodnotu, je-li instance this větší než instance obj.

Implementaci tohoto rozhraní vyžadují například metody Sort a BinarySearch.

Implementují ho veškeré základní datové typy.

public interface IComparable { int CompareTo (object obj); }

Předvádějící
Poznámky prezentace

Předdefinovaná rozhraní ◦ IComparer

Slouží ke stejnému účelu jako rozhraní IComparable. Metoda Compare má vracet: zápornou hodnotu, je-li instance x menší než instance y, nulu, je-li instance x rovna instanci y, kladnou hodnotu, je-li instance x větší než instance y.

Toto rozhraní je např. parametrem metod Sort a BinarySearch.

public interface IComparer { int Compare(object x, object y); }

Předvádějící
Poznámky prezentace

Předdefinovaná rozhraní ◦ IComparer – příklad

class Osoba { private int cislo; private string jmeno; public int Cislo { get { return cislo; } set { cislo = value; } } public string Jmeno { get { return jmeno; } set { jmeno = value; } } public Osoba(int cislo, string jmeno) { this.cislo = cislo; this.jmeno = jmeno; } public static int CompareCislo(Osoba x, Osoba y) { return x.cislo.CompareTo(y.cislo); } public static int CompareJmeno(Osoba x, Osoba y) { return x.jmeno.CompareTo(y.jmeno); } public override string ToString() { return cislo + "\t" + jmeno; } }

1

using System.Collections;

Předvádějící
Poznámky prezentace
Pr_icomparer

Předdefinovaná rozhraní ◦ IComparer – příklad

delegate int CompareOsobaCallback(Osoba x, Osoba y); class OsobaComparer : IComparer { CompareOsobaCallback compare; public OsobaComparer(CompareOsobaCallback compare) { this.compare = compare; } public int Compare(object x, object y) { return compare((Osoba)x, (Osoba)y); } }

2

Předvádějící
Poznámky prezentace
Pr_icomparer Příklad demonstruje použití rozhraní IComparer pro třídění pole prvků typu Osoba podle čísla nebo jména osoby.

Předdefinovaná rozhraní ◦ IComparer – příklad

delegate int CompareOsobaCallback(Osoba x, Osoba y); class OsobaComparer : IComparer { CompareOsobaCallback compare; public OsobaComparer(CompareOsobaCallback compare) { this.compare = compare; } public int Compare(object x, object y) { return compare((Osoba)x, (Osoba)y); } }

2

Předvádějící
Poznámky prezentace
Pr_icomparer

Předdefinovaná rozhraní ◦ IComparer – příklad

class Program { static void Vypis(Osoba[] osoby, string text) { Console.WriteLine(text); foreach (Osoba item in osoby) { Console.WriteLine(item.ToString()); } } static void Main(string[] args) { Osoba[] osoby = new Osoba[] { new Osoba(10, "Karel"), new Osoba(5, "Pavel"), new Osoba(20, "Jana"), new Osoba(1, "Soňa") }; Vypis(osoby, "Původní seznam"); Array.Sort(osoby, new OsobaComparer(Osoba.CompareCislo)); Vypis(osoby, "Utříděný seznam podle čísla"); Array.Sort(osoby, new OsobaComparer(Osoba.CompareJmeno)); Vypis(osoby, "Utříděný seznam podle jména"); Console.ReadKey(); } }

3

Předvádějící
Poznámky prezentace
Pr_icomparer

Předdefinovaná rozhraní ◦ IDisposable Toto rozhraní implementují třídy, které zapouzdřují

prostředky, jež nemohou být uvolněny automatickou správou paměti (soubory, síťová připojení, …). Tyto prostředky pak musí uvolnit samotná třída: v destruktoru volán automatickou správou paměti při uvolnění instance

nebo v metodě Dispose. volána buď přímo nebo automaticky prostřednictvím

příkazu using

Předdefinovaná rozhraní ◦ IDisposable Uživatel by měl neřízené prostředky uvolnit ve chvíli,

kdy už je nepotřebuje přímým voláním metody Dispose nebo pomocí příkazu using.

Měla by však existovat „pojistka“, která se o uvolnění postará – destruktor.

Třída implementující toto rozhraní by se měla držet zavedeného vzoru:

Předdefinovaná rozhraní ◦ IDisposable

public class ResourceHolder : IDisposable { private bool isDisposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { // Úklid řízených prostředků voláním jejich metod Dispose() } // Úklid neřízených prostředků isDisposed = true; } } ~ResourceHolder() { Dispose(false); }

1

s false je volána z destruktoru s true je volána z Dispose()

uvolní neřízené i řízené prostředky

uvolní neřízené prostředky

udává, zda instance třídy ResourceHolder byla již

zlikvidována

automatická správa paměti

-> není už potřeba volat destruktor

Předvádějící
Poznámky prezentace
Třída ResourceHolder obsahuje dvě přetížené verze metody Dispose. Metoda Dispose(bool) provádí vlastní uvolnění neřízených prostředků. Je volána z destruktoru s hodnotou false a z metody Dispose() s hodnotou true. Tento parametr určuje místo, odkud byla metoda volána. Metoda Dispose(bool) by neměla být volána ze žádného jiného místa kódu, než je uvedeno. Pokud je metoda Dispose(bool) volána z metody Dispose(), provede uvolnění neřízených i řízených prostředků této třídy. Pokud je volána z destruktoru, provede uvolnění pouze neřízených prostředků. Řízené prostředky, tj. reference na instance jiných tříd, které zapouzdřují neřízené prostředky, se v destruktoru třídy ResourceHolder neuvolňují, protože pro tyto třídy automatická správa paměti zavolá jejich destruktor, který uvolnění prostředků provede. Pokud by uživatel zavolal metodu Dispose() vícekrát, jen první její volání způsobí uvolnění prostředků, další volání již nic neprovede. Na začátku každé veřejné metody třídy ResourceHolder byl se mělo testovat, zda instance této třídy byla zlikvidována a pokud tomu tak je, měla by se vyvolat výjimka typu System.ObjectDisposedException s parametrem obsahujícím název zlikvidované třídy – viz metoda NějakáMetoda.

Předdefinovaná rozhraní ◦ IDisposable

public void NějakáMetoda() { if (isDisposed) throw new ObjectDisposedException("ResourceHolder"); // implementace metody } } public class DerivedResourceHolder : ResourceHolder { protected override void Dispose(bool disposing) { if (disposing) { // Úklid řízených prostředků voláním jejich metod Dispose() } // Úklid neřízených prostředků base.Dispose(disposing); } }

2

Předdefinovaná rozhraní ◦ IDisposable a automatická správa paměti V .NET se instance referenčních typů vytváří na řízené haldě

– virtuální část paměti vyhrazena procesu fungující podobně jako zásobník rozdělen do dvou bloků – obsazené a volné paměti. Volná paměť se vždy přiřazuje z vrcholu (rychlost).

Při úklidu se z haldy nejprve odstraní veškeré objekty, ne které už neexistují žádné reference. Poté se přeskupí halda tak, aby bloky volné paměti tvořily souvislý celek. Přitom aktualizuje existující reference na nové adresy objektů.

Např. po zrušení mnoha objektů lze zavolat úklid paměti i explicitně zavoláním metody Collect třídy GC.

Předdefinovaná rozhraní ◦ IFormattable public interface IFormattable {

string ToString (string format, IFormatProvider formatProvider);

}

Toto rozhraní implementují typy, které umožňují formátování při konverzi na řetězec znaků. Jedná se např. o všechny základní datové typy v C#.

Předdefinovaná rozhraní ◦ IEnumerable public interface IEnumerator {

object Current { get; }

bool MoveNext();

void Reset();

} Popis složek viz příkaz foreach.

Předdefinovaná rozhraní ◦ Další rozhraní ICollection – implementují jej všechny kolekce včetně polí.

Poskytuje metodu CopyTo pro kopírování části kolekce, vlastnost Count udávající počet prvků kolekce aj.

IList – implementují jej kolekce, s nimiž lze zacházet pomocí indexování, podobně jako s poli, jako např. třída ArrayList.

IDictionary – obsahuje vlastnosti typické pro datovou strukturu zvanou slovník (v C++ párový asociativní kontejner).

Generická rozhraní – viz později.

Blok příkazů, který poskytuje posloupnost hodnot.

Na rozdíl od běžného bloku příkazů obsahuje jeden nebo více příkazů yield.

Syntaxe yield: ◦ yield return výraz ; ◦ yield break ;

Iterátor může být použit jako tělo metody, tělo přístupové metody vlastnosti

nebo tělo přetíženého operátoru, přičemž návratový typem této metody, vlastnosti nebo operátoru musí být IEnumerator nebo IEnumerable nebo jejich generické obdoby.

Jednodušší způsob pro implementaci rozhraní IEnumerator

(IEnumerable).

Poskytuje následující hodnotu iterace

Indikuje, že iterace je kompletní

Příklad class Zasobnik : IEnumerable { object[] pole = new object[100]; int pocet; public void Vloz(object i) { if (pocet < pole.Length) pole[pocet++] = i; } public object Odeber() { ... } public IEnumerator GetEnumerator() { for (int i = pocet - 1; i >= 0; --i) yield return pole[i];; } } class Program { static void Main(string[] args) { Zasobnik zasobnik = new Zasobnik(); for (int i = 0; i < 10; i++) zasobnik.Vloz(i); foreach (int i in zasobnik) Console.Write("{0} ", i); Console.WriteLine(); } }

Předvádějící
Poznámky prezentace
Pr_iterator Třída Zásobník představuje zásobník libovolných prvků. Implementuje rozhraní IEnumerable deklarováním metody GetEnumerator. Tato metoda obsahuje iterátor, který prochází prvky zásobníku od posledního k prvnímu (od vrcholu ke spodku). Výstup programu bude následující: 9 8 7 6 5 4 3 2 1 0

Příklad 2 class Zasobnik : IEnumerable { // dříve uvedené složky – viz předchozí případ public IEnumerable OdVrcholu { get { return this; } } public IEnumerable OdSpodku { get { for (int i = 0; i < pocet; i++) yield return pole[i]; } } } class Program { static void Main(string[] args) { Zasobnik zasobnik = new Zasobnik(); for (int i = 0; i < 10; i++) zasobnik.Vloz(i); foreach (int i in zasobnik.OdSpodku) Console.Write("{0} ", i); Console.WriteLine(); foreach (int i in zasobnik.OdVrcholu) Console.Write("{0} ", i); Console.WriteLine(); } }

Předvádějící
Poznámky prezentace
Pr_iterator2 Vlastnost OdVrcholu poskytuje rozhraní pro procházení prvků zásobníku od vrcholu ke spodku, tj. stejně jako rozhraní, které vrací metoda GetEnumerator. Tato vlastnost může tedy pouze vrátit this, jelikož třída Zasobnik implementuje rozhraní IEnumerable s požadovaným způsobem iterace. Vlastnost OdSpodku poskytuje rozhraní pro procházení prvků zásobníku od spodku k vrcholu. Přístupová metoda get je implementována pomocí iterátoru. Výstup programu bude následující: 0 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 0

Příklad 3

class Program { static void Vypis(IEnumerable kolekce) { foreach (int i in kolekce) Console.Write("{0} ", i); Console.WriteLine(); } static void Main(string[] args) { Zasobnik zasobnik = new Zasobnik(); for (int i = 0; i < 10; i++) zasobnik.Vloz(i); Vypis(zasobnik.OdSpodku); Console.ReadKey(); } } Uvedené vlastnosti mohou být

také použity mimo příkaz foreach

Příklad 4 class Program { static IEnumerable Posloupnost(int od, int @do, int krok) { for (int i = od; i <= @do; i += krok) { yield return i; } } static void Vypis(IEnumerable kolekce) { foreach (int i in kolekce) Console.Write("{0} ", i); Console.WriteLine(); } static void Main(string[] args) { Vypis(Posloupnost(10, 20, 2)); Console.ReadKey(); } }

Iterátor může obsahovat i metoda, která má parametry

(ne však ref či out).

Předvádějící
Poznámky prezentace
Pr_iterator_posloupnost Výpis programu bude následující 10 12 14 16 18 20

Příklad 5 class Program { static IEnumerable Posloupnost(int od, int @do) { while (od <= @do) yield return od++; } static void Main(string[] args) { IEnumerable ie = Posloupnost(1, 5); foreach (int x in ie) { foreach (int y in ie) { Console.Write("{0,3} ", x * y); } Console.WriteLine(); } Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_iterator_parametry Příklad vypíše tabulku násobků čísel 1 až 5. Metoda Posloupnost je vyvolána jen jednou k vytvoření objektu rozhraní ie. Avšak metoda ie.GetEnumerator() je volána dvakrát dvěma vnořenými příkazy foreach. Vytvoří se tudíž dva samostatné objekty enumerátorů. Tyto objekty zapouzdřují kód iterátoru metody Posloupnost, jenž modifikuje parametr od. Objekty enumerátorů si však uchovávají svojí vlastní kopii parametrů od a @do. Výpis programu bude následující: 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25

Odlišnosti oproti C++ ◦ Kromě handlerů může být k bloku try připojena i koncovka finally (používá se ve strukturovaných výjimkách jazyka C v prostředí Win32 API)

◦ K přenosu informací o výjimce lze použít pouze třídu odvozenou od System.Exception

◦ Blok try nemůže tvořit tělo metody či konstruktoru Syntaxe ◦ Podobná jako v C++ throw výraznep ; Výraz – výraz, jehož výsledkem je instance třídy System.Exception nebo jejího potomka. Často se jedná o vytvoření nové instance pomocí operátoru new, např. throw new Exception("Nastala výjimka");

Syntaxe ◦ Podobná jako v C++ throw výraznep ; Pokud je příkaz throw v těle handleru, výraz lze vynechat. V

takovém případě throw znovu vyvolá výjimku, kterou handler zachytil.

Za blokem try může nasledovat jeden nebo více handlerů a maximálně jedna koncovka finally.

Syntaxe try { příkazynep } seznam_handlerůnep koncovka try { příkazynep } seznam_handlerů

Syntaxe handleru catch ( typ identifikátornep ) blok catch blok Není-li uveden typ, jedná se o syntaktickou

zkratku pro univerzální handler: catch (System.Exception) { ... }

Syntaxe ◦ Univerzální handler – neposkytuje informaci o

výjimce; musí být posledním v seznamu handlerů. ◦ Koncovka – blok příkazů (začínající klíčovým slovem finally), který se provede vždy bez ohledu na výsledek bloku try.

Vznik a šíření ◦ Výjimku lze vyvolat jednak příkazem throw a jednak je

vyvolána v několika dalších případech: příkaz checked, nesprávné přetypování, při překročení mezí

polí, špatné volání metod z BCL, … ◦ Vznikne-li výjimka, začne program hledat odpovídající

handler. Pokud program neobsahuje koncovky, chová se následovně: Po vzniku výjimky program přeskočí všechny zbývající

příkazy v aktuálním bloku. Jedná-li se o blok try, začne po řadě hledat vhodný handler, který může výjimku ošetřit. O tom, zda handler výjimku zachytí, rozhoduje typ handleru. Přitom se uplatňuje pravidlo, že potomek může zastoupit předka.

Vznik a šíření ◦ Výjimku lze vyvolat jednak příkazem throw a jednak je

vyvolána v několika dalších případech: příkaz checked, nesprávné přetypování, při překročení mezí

polí, špatné volání metod z BCL, … ◦ Vznikne-li výjimka, začne program hledat odpovídající

handler. Pokud program neobsahuje koncovky, chová se následovně: Po vzniku výjimky program přeskočí všechny zbývající

příkazy v aktuálním bloku. Jedná-li se o blok try, začne po řadě hledat vhodný handler, který může výjimku ošetřit. O tom, zda handler výjimku zachytí, rozhoduje typ handleru. Přitom se uplatňuje pravidlo, že potomek může zastoupit předka.

Vznik a šíření ◦ Pokračování popisu programu se vzniklou výjimkou Vznikne-li výjimka v jiném bloku než bloku try nebo k

němu není připojen vhodný handler, přejde program do dynamicky nadřízeného bloku. To znamená, že vznikne-li výjimka v těle metody, opustí program tuto metodu a přejde do metody, která ji zavolala. Opět přeskočí zbylé příkazy v aktuálním bloku a bude u něj hledat vhodný handler.

Pokud program najde vhodný handler, přejde do něj a provede jeho příkazy. Po jejich vykonání přeskočí případné další handlery a bude pokračovat za příkazem try.

Vznik a šíření ◦ Pokračování popisu programu se vzniklou výjimkou Jestliže program nenajde vhodný handler ani v metodě Main,

předá program výjimku prostředí .NET Framework, které vypíše zprávu o chybě a program ukončí.

Po vstupu do handleru se výjimka považuje za ošetřenou. To znamená, že tělo handleru může být i prázdné.

◦ Uvedené handlery je potřeba uvádět v pořadí od nejvíce odvozeného typu po typ, který je předkem všech předchozích odvozených typů.

Koncovka ◦ Příkazy bloku finally Provede se vždy po bloku try (který může skončit

klasicky, příkazem skoku [break, goto, continue, return], výjimkou)

◦ V koncovce se nesmí objevit return.

Příklad class Program { //const int n = 10; const int n = 1; static void f(int i) { Console.WriteLine("Začátek f({0})", i); if (i == n) throw new Exception(); Console.WriteLine("Konec f({0})", i); } static void Main(string[] args) { try { for (int i = 0; i < 2; i++) { Console.WriteLine("Před voláním f"); f(i); Console.WriteLine("Za voláním f"); } } catch (ArithmeticException) { Console.WriteLine("Aritmetická výjimka"); } catch (Exception) { Console.WriteLine("Obecná výjimka"); } Console.WriteLine("Konec programu"); Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_vyjimka Pokud konstanta n bude mít hodnotu 10, výjimka v programu nevznikne a výstup programu bude následující: Před voláním f Začátek f(0) Konec f(0) Za voláním f Před voláním f Začátek f(1) Konec f(1) Za voláním f Konec programu Pokud konstanta n bude mít hodnotu 1, při druhém volání funkce f vznikne výjimka typu Exception a výstup programu bude následující: Před voláním f Začátek f(0) Konec f(0) Za voláním f Před voláním f Začátek f(1) Obecná výjimka Konec programu

Příklad 2

class Program { const int n = 1; static void f(int i) { Console.WriteLine("Začátek f({0})", i); if (i == n) throw new Exception(); Console.WriteLine("Konec f({0})", i); }

1

Předvádějící
Poznámky prezentace
Pr_koncovka Výstup programu bude následující: Před voláním f Začátek f(0) Konec f(0) Za voláním f Před voláním f Začátek f(1) Koncovka vnitřního bloku Obecná výjimka Koncovka vnějšího bloku Konec programu

Příklad 2

static void Main(string[] args) { try { try { for (int i = 0; i < 2; i++) { Console.WriteLine("Před voláním f"); f(i); Console.WriteLine("Za voláním f"); } } catch (ArithmeticException) { Console.WriteLine("Aritmetická výjimka"); } finally { Console.WriteLine("Koncovka vnitřního bloku"); } } catch { Console.WriteLine("Obecná výjimka"); } finally { Console.WriteLine("Koncovka vnějšího bloku"); } Console.WriteLine("Konec programu"); Console.ReadKey(); } } // class Program

2

Předvádějící
Poznámky prezentace
Pr_koncovka Výstup programu bude následující: Před voláním f Začátek f(0) Konec f(0) Za voláním f Před voláním f Začátek f(1) Koncovka vnitřního bloku Obecná výjimka Koncovka vnějšího bloku Konec programu

Třídy ◦ Knihovna BCL obsahuje několik předdefinovaných

tříd, od nichž lze založit instance výjimky. ◦ Lze deklarovat vlastní, která bude potomkem System.Exception nebo jejího potomka. ◦ Třída Exeption Konstruktory

public Exception()

public Exception(string message)

public Exception(string message, Exception innerException)

Vytvoří instanci s implicitním textem chyby

Vytvoří instanci s textem chyby message

Vytvoří instanci s textem chyby message a nastaví vnitřní výjimku

Třídy ◦ Třída Exeption Vlastnosti

public virtual string Message { get; }

public virtual string StackTrace { get; }

public MethodBase TargetSite { get; }

public virtual string Source { get; set; }

Poskytuje text chyby, který do instance uložil

konstruktor

Řetězec poskytující obsah zásobníku – seznam volaných metod od vzniku výjimky po její zachycení (nebere ohled na rekurzivní volání)

Poskytuje informace o, která výjimku vyvolala.

Poskytuje/nastavuje název aplikace nebo objektu, který výjimku způsobil

Třídy ◦ Třída Exeption Vlastnosti

public virtual string HelpLink { get; set; }

public virtual IDictionary Data { get; }

public Exception InnerException { get; }

Poskytuje/nastavuje název souboru nápovědy, v němž jsou další informace o dané výjimce.

Poskytuje uživatelská data o výjimce uložená ve slovníku.

Poskytuje referenci na instanci tzv. vnitřní výjimky.

Třídy ◦ Třída Exeption Pokud lze v určité části programu, kde se podařilo

zachytit výjimku, tuto ošetřit jen částečně, k úplnému vyřešení je potřeba tutéž nebo jinou výjimku vyvolat znovu: throw; //vyvolá tutéž výjimku

Potřebujeme-li vyvolat výjimku jiného typu a přesto zachovat informace o původní výjimce: catch (ArithmeticException e) { throw new Exception("Něco se stalo", e); }

U takto zachycené výjimky může handler pomocí InnerException získat informace o původní příčině

chyby.

Příklad – vnitřní výjimka class Matematika { public static int Faktorial(int n) { int s = 1; while (n > 0) s = checked(s * n--); return s; } } class Program { static void f(int cislo) { Console.WriteLine("{0}! = {1}", cislo, Matematika.Faktorial(cislo)); }

1

Předvádějící
Poznámky prezentace
Pr_innerException Pokud se program spustí ve složce, v němž se nacházejí zdrojové soubory, výstup programu bude následující: Arithmetic operation resulted in an overflow. at Vyjimky03.Matematika.Faktorial(Int32 n) at Vyjimky03.Program.f(Int32 cislo) at Vyjimky03.Program.Main(String[] args) Jinak výpis bude obsahovat i soubory včetně cesty, v nichž se uvedené metody nacházejí.

Příklad – vnitřní výjimka static void Main(string[] args) { try { f(100); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); if (e.InnerException != null) { Console.WriteLine("Vnitřní výjimka:"); Console.WriteLine(e.InnerException.Message); Console.WriteLine(e.InnerException.StackTrace); } } Console.ReadKey(); } }

2

Předvádějící
Poznámky prezentace
Pr_innerException Pokud se program spustí ve složce, v němž se nacházejí zdrojové soubory, výstup programu bude následující: Arithmetic operation resulted in an overflow. at Vyjimky03.Matematika.Faktorial(Int32 n) at Vyjimky03.Program.f(Int32 cislo) at Vyjimky03.Program.Main(String[] args) Jinak výpis bude obsahovat i soubory včetně cesty, v nichž se uvedené metody nacházejí.

Příklad – vnitřní výjimka 2 – úprava f()

static void f(int cislo) { try { Console.WriteLine("{0}! = {1}", cislo, Matematika.Faktorial(cislo)); } catch (OverflowException e) { throw new Exception("Nepovedlo se spočítat faktoriál", e); } }

Předvádějící
Poznámky prezentace
Pr_innerException2 výstup programu bude následující: Nepovedlo se spočítat faktoriál at Vyjimky03.Program.f(Int32 cislo) at Vyjimky03.Program.Main(String[] args) Vnitřní výjimka: Arithmetic operation resulted in an overflow. at Vyjimky03.Matematika.Faktorial(Int32 n) at Vyjimky03.Program.f(Int32 cislo)

Další třídy ◦ System.Exception se používá zřídka, častěji se

používají třídy odvozené Má dva přímé potomky System.SystemException – práce s výjimkami ve

jmenném prostoru System. Výjimky vyvolané .NET či programátorovým kódem.

System.ApplicationException – třída pro aplikační nefatální chyby.

Další třídy ◦ Od System.SystemException je odvozena řada běžně

používaných tříd výjimek. System.ArithmeticException – pro výpočtové chyby

Má potomky System.DivideByZeroException a System.OverflowException

System.IndexOutOfRangeException - pokud index

prvku pole je mimo meze tohoto pole.

Vznikne při dělení nulou

Vznikne při přetečení celočíselného typu, je-li

kontrolováno

Další třídy ◦ System.TypeInitializationException – pokud se

rozšíří výjimka ze statického konstruktoru ◦ System.RankException – pokud se použije pole s

nesprávným počtem rozměrů ◦ System.InvalidOperationException - je-li volání

metody v daném kontextu chybné. Např. MoveNext v enumerátoru, pokud došlo ke změně prvků v

kolekci po vytvoření instance enumerátoru. Potomek System.ObjectDisposedException – při pokusu

provést operaci s objektem u něhož už byla volána metoda Dispose.

Další třídy ◦ System.NullReferenceException – při přístupu ke složce

objektového typu pomocí reference o hodnotě null ◦ System.ArgumentException – předkem výjimek, které

vzniknou při volání metody s chybným parametrem. Kromě konstruktorů z třídy Exception má další konstruktory s

parametrem string paramName, který udává jméno parametru, který výjimku způsobil. Ten je přístupný přes vlastnost ParamName.

System.ArgumentNullException – parametr má hodnotu null. System.ArgumentOutOfRangeException – parametr je mimo rozsah. System.ComponentModel.InvalidEnumArgumentException –

parametr výčtové-ho typu s nesprávnou hodnotou.

Další třídy ◦ System.NotImplementedException – vznikne při

volání metody, která není implementována. ◦ System.ArrayTypeMismatchException – vznikne při

pokusu uložit do prvku pole hodnotu nesprávného typu.

◦ System.OutOfMemoryException – vznikne při nedostatku paměti pro pokračování v běhu programu.

◦ System.FormatException – vznikne v metodě určené pro formátování hodnoty (řetězce), pokud skutečné parametry neodpovídají zadanému formátu. Výjimku vyvolá např. metoda Console.WriteLine nebo string.Format, pokud počet skutečných parametrů neodpovídá počtu parametrů specifikovaných ve formátovacím řetězci.

Další třídy ◦ System.StackOverflowException – vznikne při přetečení

paměti zásobníku. K přetečení může nastat např. při neustálém rekurzivním volání metody.

◦ System.IO.IOException – je předkem výjimek, které vznikají při vstupních a výstupních operacích. System.IO.DirectoryNotFoundException – vznikne při

přístupu k neexistujícímu adresáři. System.IO.EndOfStreamException – vznikne při pokusu

čtení za koncem souboru. System.IO.FileNotFoundException – vznikne při přístupu

k neexistujícímu souboru. System.IO.FileLoadException – vznikne, pokud nelze

načíst existující sestavení. System.IO.PathTooLongException – vznikne, pokud

jméno souboru nebo cesty je delší než systémem definované maximum.

Možnosti ladění ve Visual Studiu ◦ Menu Debug | Exceptions - vyvolané dialogové

okno obsahuje seznam předefinovaných výjimek. Každá má dva checkboxy: Thrown – program se pozastaví, pokud dojde k

vyvolání výjimky, a to na místě vzniku výjimky. User-unhandled – program se pozastaví, pokud

došlo k vyvolání výjimky, pro níž nebyl nalezen vhodný handler, a to na místě vzniku výjimky.

Do seznamu výjimek lze přidat i uživatelem definovanou výjimku pomocí tlačítka Add nebo ji odstranit pomocí tlačítka Delete.

Doporučení ◦ Metody by v případě vzniku chyby neměly vracet

informaci o chybě (kód chyby), ale měly by vyvolat výjimku.

◦ Veřejné a chráněné metody, které vyvolávají výjimky, by měly být opatřeny dokumentačním komentářem obsahujícím seznam výjimek s popisem případů jejich vzniku.

◦ Veřejná metoda by neměla obsahovat parametr, který by udával, zda se má nějaká výjimka vyvolat.

Doporučení ◦ Z koncovky by se neměla vyvolat výjimka explicitně, tj.

uvedením příkazu throw. Může být vyvolána implicitně, jako výsledek volání nějaké metody.

◦ Při opětovném vyvolání výjimky v handleru, který ji zachytil, se má použít prázdný příkaz throw a ne příkaz throw s instancí vyvolávané výjimky, aby se zabránilo vzniku výjimky související se zásobníkem. Např.

try { f(10); } catch (ArithmeticException e) { // ... // throw e; // Nespravně throw; // Správně }

Doporučení ◦ Nedoporučuje se vyvolávat výjimky typu Exception nebo SystemException. Měly by se vyvolávat výjimky odvozených typů, které odpovídají charakteru chyby.

◦ Výjimka typu ArgumentException a její potomci by měly být vytvořeny pomocí konstruktoru s parametrem paramName, který udává jméno parametru, jenž výjimku způsobil. Např. if (paramA == null) {

throw new ArgumentNullException("paramA", "text chyby"); // Nebo jen jméno parametru: // throw new ArgumentNullException("paramA"); } Pokud se tato výjimka vyvolává z přístupové

metody set vlastnosti, jako jméno parametru se má použít text "value".

Uživatelem definované ◦ Měly by být odvozeny od třídy Exception nebo

od jiné běžné základní třídy pro výjimky. ◦ Jméno uživatelem definované výjimky má mít

příponu Exception, tak jako je tomu u předdefinovaných tříd výjimek. ◦ Výjimky by měly být serializovatelné. ◦ Výjimky by měly obsahovat alespoň konstruktory

třídy Exception tři veřejné jeden chráněný, určený pro serializaci

Rozdíly oproti C++ ◦ Mimo indexerů se všechny operátory musí

deklarovat jako statické veřejné metody tříd (struktur). ◦ Množina přetěžovaných operátorů je v C#

podstatně menší. ◦ Přetížené operátory nelze volat zápisem

operátorové fce. ◦ Lze přetěžovat následující unární a binární

operátory: + – ! ~ ++ -- true false + - * / % & | ^ << >> == != > < >= <=

Syntaxe ◦ public static typ operator symbol ( seznam_parametrů ) tělo ◦ static public typ operator symbol ( seznam_parametrů ) tělo Typ, seznam parametrů a tělo – stejný význam jako v deklaraci jiné metody Symbol – symbol operátoru

◦ unární operátory – mají jeden parametr ◦ binární operátory – mají dva parametry, první představuje levý operand,

druhý pravý operand

Parametry operátoru lze předávat pouze hodnotou

Třída nebo struktura může obsahovat několik přetížených binárních operátorů se stejným symbolem lišících se svými

parametry.

Některé operátory tvoří logické dvojice – nutno přetížit vždy oba operátory

Složené přiřazovací operátory +=, -= a další nelze přetěžovat. Překladač ovšem odvozuje jejich význam od významu operátorů +, - a dalších. Takže např. přetížením binárního operátoru + pro typ T se zároveň definuje význam operátoru +=.

== !=

< >

<= >=

true false

Nelze přetěžovat operátory || a &&. ◦ Překladač odvozuje jejich význam od | a &. ◦ Lze je používat pouze v případě, že návratový typ a oba

operandy jsou stejného typu.

Příklad public class Matice { int[,] a; public Matice(int m, int n) { a = new int[m, n]; } public static Matice Generuj(int m, int n) { Matice matice = new Matice(m, n); Random random = new Random(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { matice.a[i, j] = random.Next(0, 100); } } return matice; } public void Vypis() { for (int i = 0; i < a.GetLength(0); i++) { for (int j = 0; j < a.GetLength(1); j++) { Console.Write("{0,6}", a[i, j]); } Console.WriteLine(); } Console.WriteLine(); }

1

Předvádějící
Poznámky prezentace
Pr_operatory_matice Třída Matice obsahuje operátor násobení matice celým číslem #1. Tento operátor nejprve vytvoří novou instanci matice, mající stejnou velikost jako matice m, která reprezentuje levý operand tohoto operátoru. Potom vynásobí každý prvek matice m zadaným celým číslem a tyto výsledky uloží do nové matice. Nakonec vrátí novou matici. Matice m se nezmění. Aby bylo možné provést násobení s prohozenými operandy, tj. levým operandem by bylo číslo a pravým operandem matice, obsahuje třída Matice ještě jeden operátor násobení #2, který pouze zavolá operátor #1. V příkazu #3 se volá operátor #1 a v příkazu #4 operátor #2.

Příklad public static Matice operator *(Matice m, int hodnota) // #1 { Matice matice = new Matice(m.a.GetLength(0), m.a.GetLength(1)); for (int i = 0; i < m.a.GetLength(0); i++) { for (int j = 0; j < m.a.GetLength(1); j++) { matice.a[i, j] = m.a[i, j] * hodnota; } } return matice; } public static Matice operator *(int hodnota, Matice m) // #2 { return m * hodnota; } } class Program { static void Main(string[] args) { Matice matice = Matice.Generuj(2, 3); matice.Vypis(); matice *= 10; // #3 - volá se operátor #1 matice.Vypis(); matice = 10 * matice; //#4 - volá se operátor #2 matice.Vypis(); Console.ReadKey(); } }

2

Předvádějící
Poznámky prezentace
Pr_operatory_matice Třída Matice obsahuje operátor násobení matice celým číslem #1. Tento operátor nejprve vytvoří novou instanci matice, mající stejnou velikost jako matice m, která reprezentuje levý operand tohoto operátoru. Potom vynásobí každý prvek matice m zadaným celým číslem a tyto výsledky uloží do nové matice. Nakonec vrátí novou matici. Matice m se nezmění. Aby bylo možné provést násobení s prohozenými operandy, tj. levým operandem by bylo číslo a pravým operandem matice, obsahuje třída Matice ještě jeden operátor násobení #2, který pouze zavolá operátor #1. V příkazu #3 se volá operátor #1 a v příkazu #4 operátor #2.

Rovná se / nerovná se ◦ == != ◦ statické metody třídy object: Equals a ReferenceEquals ◦ Význam pro třídy (referenční typy) ReferenceEquals vrací true, pokud se obě reference odkazují na stejnou instanci

nebo mají-li obě hodnotu null.

Virtuální metoda Equals vrací true, pokud se obě reference odkazují na stejnou instanci. Skutečný parametr metody může být i null – v tom případě metoda vrací false. Potomek však může tuto metodu předefinovat tak, že bude porovnávat složky třídy.

Statická metoda Equals nejprve zjišťuje, zda některý její parametr je null. Pokud mají oba parametry hodnotu null, vrací true. Pokud jeden z nich má hodnotu null, vrací false. Pokud ani jeden nemá hodnotu null, vrací výsledek volání virtuální metody Equals pro skutečný objekt, tj. případně volá její předefinovanou verzi.

Operátor == provádí stejnou funkci jako metoda ReferenceEquals. Lze jej však pro danou třídu přetížit.

Rovná se / nerovná se ◦ == != ◦ statické metody třídy object: Equals a ReferenceEquals ◦ Význam pro struktury (hodnotové typy) Metoda ReferenceEquals vrací false, vždy.

Virtuální metoda Equals volá metodu Equals pro všechny datové

složky struktury, tj. i pro složky typu nějaké struktury nebo třídy. Ve skutečnosti tuto činnost provádí předefinovaná verze této metody v třídě System.ValueType, která je předkem všech hodnotových typů. V uživatelem definované struktuře lze metodu předefinovat.

Statická metoda Equals zavolá virtuální metodu Equals.

Operátor == není pro uživatelem definovanou strukturu implicitně definován. Lze jej však přetížit.

Doporučení pro == a metodu Equals ◦ Pokud předefinujeme metodu Equals, měli bychom předefinovat i

metodu GetHashCode třídy object. Jinak překladač hlásí varování. ◦ Pokud se v třídě nebo struktuře přetíží operátor ==, měla by se

předefinovat i metoda Equals. Jinak překladač hlásí varování.

◦ Ve většině tříd by se neměl přetěžovat operátor ==, i když třída obsahuje předefinovanou metodu Equals. Operátor == by se měl přetížit jen v třídě, která představuje základní datový typ, jako je tomu např. u třídy string.

V takovém případě by operátor == a metoda Equals měly provádět stejnou činnost. To se zpravidla řeší tak, že operátor == volá jednu z metod Equals třídy object. Přetížený operátor == třídy by měl v takovém případě volat statickou

metodu Equals, která řeší případ, kdy některý z parametrů má hodnotu null.

Příklad

struct A { int x; public A(int x) { this.x = x; } } class B { int y; public B(int y) { this.y = y; } public override bool Equals(object obj) { B other = obj as B; if (other == null) return false; return y == other.y; } } struct C { A a; B b; int z; public C(A a, B b, int z) { this.a = a; this.b = b; this.z = z; } public static bool operator ==(C left, C right) { return left.Equals(right); } public static bool operator !=(C left, C right) { return !left.Equals(right); } }

1

Předvádějící
Poznámky prezentace
Pr_operatory_rovnase_struktury Příkaz #1 volá přetížený operátor == struktury C, který volá metodu Equals třídy object. Ta volá metodu Equals pro všechny datové složky struktury. Třída B má předefinovanou metodu Equals a proto příkaz #1 vypíše hodnotu True. Pokud by v třídě B nebyla metoda Equals předefinována, příkaz #1 by vypsal hodnotu False. Pokud by ve struktuře C nebyl operátor == přetížen, příkaz #1 by překladač označil za chybný. V příkazu #2 se porovnávají reference na instance dvou tříd pomocí standardního operátoru ==, která porovnává odkazy na tyto instance a tudíž tento příkaz vypíše hodnotu False. Pokud by se přetížil operátor == pro třídu B tak, že by volal statickou metodu Equals public static bool operator ==(B left, B right) { return Equals(left, right); } příkaz #2 by vypsal hodnotu True.

Příklad

class Program { static void Main(string[] args) { B b = new B(20), b2 = new B(20); C c = new C(new A(10), b, 30); C c2 = new C(new A(10), b2, 30); Console.WriteLine(c == c2); // #1 Console.WriteLine(b == b2); // #2 Console.ReadKey(); } }

2

Předvádějící
Poznámky prezentace
Pr_operatory_rovnase_struktury Příkaz #1 volá přetížený operátor == struktury C, který volá metodu Equals třídy object. Ta volá metodu Equals pro všechny datové složky struktury. Třída B má předefinovanou metodu Equals a proto příkaz #1 vypíše hodnotu True. Pokud by v třídě B nebyla metoda Equals předefinována, příkaz #1 by vypsal hodnotu False. Pokud by ve struktuře C nebyl operátor == přetížen, příkaz #1 by překladač označil za chybný. V příkazu #2 se porovnávají reference na instance dvou tříd pomocí standardního operátoru ==, která porovnává odkazy na tyto instance a tudíž tento příkaz vypíše hodnotu False. Pokud by se přetížil operátor == pro třídu B tak, že by volal statickou metodu Equals public static bool operator ==(B left, B right) { return Equals(left, right); } příkaz #2 by vypsal hodnotu True.

Inkrementace a dekrementace ◦ Přetěžují se jedinou verzí, která se použije pro postfixovou

i prefixovou variantu. ◦ Parametr i návratový typ musejí být stejného typu. ◦ Jelikož parametr je předáván hodnotou, lze modifikovat

pouze novou instanci, kterou operátor vrací.

Příklad

class A { int x; public A(int x) { this.x = x; } public static A operator ++(A a) { return new A(a.x + 1); } public override string ToString() { return x.ToString(); } } class Program { static void Main(string[] args) { A a1 = new A(10), a2; a2 = ++a1; Console.WriteLine("a1 = {0}, a2 = {1}", a1, a2); a2 = a1++; Console.WriteLine("a1 = {0}, a2 = {1}", a1, a2); Console.ReadKey(); } }

1

Předvádějící
Poznámky prezentace
Pr_operatory_in-dekrementace

true a false ◦ Lze je přetížit pro typ T ◦ Následně je lze použít v if, for, while, do, ?: ◦ Návratovým typem vždy musí být bool

Příklad

class A { int x; public A(int x) { this.x = x; } public static bool operator true(A a) { return a.x != 0; } public static bool operator false(A a) { return a.x == 0; } } class Program { static void Main(string[] args) { A a = new A(10); if (a) Console.WriteLine("Instance vrací true"); // Následující příkazy jsou chybné // bool b = a; // #1 // if (!a) Console.WriteLine("Instance vrací false"); // #2 // if (a == false) Console.WriteLine("Instance vrací false"); // #3 } }

1

Předvádějící
Poznámky prezentace
Pr_true_false Program vypíše text "Instance vrací true". Příkazy #1, #2 a #3 jsou chybné. Příkaz #1 vyžaduje implicitní konverzi z typu A na typ bool. To lze zajistit definováním konverzního operátoru (viz dále) pro třídu A. Aby bylo možné použít příkazy #2 a #3, musely by být ve třídě A definovány operátory ! a == nebo konverzní operátor.

Konverzní operátory ◦ Konverzní operátory umožňují definovat způsob převodu

jednoho typu na jiný. Lze definovat implicitní a explicitní konverzní operátor.

◦ Syntaxe public static implicit operator cílový_typ ( zdrojový_typ parametr ) tělo static public implicit operator cílový_typ ( zdrojový_typ parametr ) tělo public static explicit operator cílový_typ ( zdrojový_typ parametr ) tělo static public explicit operator cílový_typ ( zdrojový_typ parametr ) tělo

Cílový typ – jméno typu, na který se konverze provede. Zdrojový typ – jméno typu, ze kterého se provede konverze na cílový typ. Parametr – identifikátor parametru zdrojového typu.

Klíčová slova implicit a explicit nejsou součástí signatury konverzního operátoru. To znamená, že nelze deklarovat

implicitní i explicitní konverzní operátor se stejnými zdrojovými a cílovými typy.

Konverzní operátory – příklad class A { int x; public A(int x) { this.x = x; } public static implicit operator int(A a) { return a.x; } public static explicit operator A(int x) { return new A(x); } } class Program { static void Main(string[] args) { A a = new A(10); int i = a; // #1 a = (A)10; // #2 } } }

Předvádějící
Poznámky prezentace
Pr_konverzni_operator V třídě A je definován implicitní konverzní operátor z typu A na typ int a explicitní konverzní operátor z typu int na typ A. Přiřazení instance třídy A do proměnné typu int v příkazu #1 je správné a nevyžaduje explicitní přetypování pomocí operátoru (typ). Zatímco opačné přiřazení v příkazu #2 lze provést pouze pomocí operátoru přetypování.

Indexery ◦ Deklarují se jako nestatické vlastnosti ◦ Jejich jméno je vyjádřeno klíčovým slovem this. ◦ Podobně jako operátor [] v C++ slouží zpravidla ke

zpřístupnění určitého prvku kolekce. ◦ Syntaxe

modifikátorynep typ this [ seznam_parametrů ] deklarace_přístupových_metod modifikátorynep typ typ_rozhraní . this [ seznam_parametrů ] deklarace_přístupových_metod

deklarace_přístupových_metod: { část_get část_setnep } { část_set část_getnep }

Indexery – příklad class Matice { int[,] a; public Matice(int m, int n) { a = new int[m, n]; } public int this[int radek, int sloupec] { get { return a[radek, sloupec]; } set { a[radek, sloupec] = value; } } } class Program { static void Main(string[] args) { Matice a = new Matice(2, 3); a[0, 0] = 10; Console.WriteLine(a[0, 0]); } }

Předvádějící
Poznámky prezentace
Pr_indexer Je definována třída Matice, zapouzdřující matici celých čísel. Pro přístup k prvku matice na zadaném řádku a sloupci je definován indexer.

Indexery ◦ Modifikátory – modifikátory přístupových práv a modifikátory

vyjadřující, zda jde o virtuální, abstraktní, zapečetěný, předefinovaný nebo zastiňující indexer. Nelze použít modifikátor static.

◦ Typ – typ prvku, který indexer vrací nebo nastavuje.

◦ Typ rozhraní – typ rozhraní, který třída nebo struktura (ve které je indexer definován) implementuje. Uvede při explicitní implementaci indexeru deklarovaného v rozhraní.

◦ Seznam parametrů – seznam formálních parametrů ve stejném tvaru jako u metod s tím rozdílem seznam musí obsahovat alespoň jeden parametr musí se jednat o parametry předávané hodnotou (nelze použít

modifikátory ref a out).

Soubory ◦ Statická třída System.IO.File ◦ Nestatická System.IO.FileInfo ◦ Výčtové typy specifikující režim souborů (otevření) Výčtový typ FileMode specifikujezpůsob otevření

Výčtový typ FileAccess určuje možnosti přístupu k souboru má atribut Flags (možnosti lze kombinovat)

Výčtový typ FileShare Specifikuje možnosti sdílení souboru mezi více programy,

které lze kombinovat.

Soubory ◦ FileMode

Konstanta Popis Open Otevření existujícího souboru

Truncate Otevření existujícího souboru a vymazání jeho obsahu

Create Vytvoření nového s. Pokud již existuje, bude přemazán.

CreateNew Vytvoření nového s. Pokud již existuje, vyvolá se výjimka System.IO.IOException

OpenOrCreate Otevření existujícího souboru. Vytvoří se nový, pokud ten neexistuje.

Append Otevření souboru a přesunutí ukazatele na jeho konec

Soubory ◦ FileAccess

Konstanta Popis Read Povoluje čtení ze souboru

Write Povoluje zápis do souboru

ReadWrite Povoluje čtení i zápis z/do souboru

Soubory ◦ FileShare

Konstanta Popis None Zakazuje sdílení

Read Povoluje čtení ze sdíleného souboru

Write Povoluje zápis do sdíleného souboru

ReadWrite Povoluje čtení i zápis z/do sdíleného souboru

Delete Povoluje vymazání sdíleného souboru.

Soubory ◦ třída File obsahuje řadu veřejných statických metod přehled nejdůležitějších Jméno souboru s cestou v jednotlivých metodách může

představovat úplnou nebo relativní cestu. Relativní cesta se vztahuje k aktuálnímu pracovnímu adresáři, který poskytuje metoda GetCurrentDirectory třídy Directory

Soubory ◦ třída File - přehled nejdůležitějších metod bool Exists(string path)- Vrací true, pokud soubor path existuje.

void Delete(string path) - Vymaže soubor path.

void Move(string sourceFileName, string destFileName) - Přesune soubor sourceFileName na nové místo destFileName, které může obsahovat i nové jméno souboru.

FileStream Create(string path) - Vytvoří nový soubor path.

Soubory ◦ třída File - přehled nejdůležitějších metod FileStream Open(string path, FileMode mode) FileStream Open(string path, FileMode mode, FileAccess access)

FileStream Open(string path, FileMode mode, FileAccess access, FileShare share) Otevře soubor path v režimu mode s případným určením přístupu access a způsobu sdílení share. Použije-li se metoda bez parametru access, soubor se otevře v režimu pro čtení i zápis. Použije-li se metoda bez parametru share, soubor se otevře bez povolení sdílení.

Soubory ◦ třída File - přehled nejdůležitějších metod FileStream OpenRead(string path)

Otevře existující soubor path pro čtení. FileStream OpenWrite(string path)

Otevře existující soubor path pro zápis. void Copy(string sourceFileName, string destFileName) Zkopíruje soubor sourceFileName do souboru destFileName, který

nesmí existovat. void Copy(string sourceFileName, string destFileName, bool overwrite) Zkopíruje soubor sourceFileName do souboru destFileName.

Pokud soubor destFileName existuje a parametr overwrite je true, cílový soubor se přepíše, a pokud je false, vznikne výjimka.

Soubory ◦ třída File - přehled nejdůležitějších metod FileAttributes GetAttributes(string path)

Vrací atributy souboru path (např. skrytý, jen pro čtení apod.). void SetAttributes(string path, FileAttributes fileAttributes) Nastaví atributy souboru path na fileAttributes.

Soubory ◦ třída File - přehled nejdůležitějších metod DateTime GetCreationTime(string path)

DateTime GetLastWriteTime(string path)

DateTime GetLastAccessTime(string path)

void SetCreationTime(string path, DateTime creationTime)

void SetLastAccessTime (string path, DateTime lastAccessTime)

void SetLastWriteTime(string path, DateTime lastWriteTime)

Vrací nebo nastavuje datum a čas vytvoření, poslední úpravy nebo přístupu k souboru path.

Soubory ◦ třída FileInfo

Třída FileInfo nabízí téměř stejné operace jako třída File. Ovšem je nutné nejprve vytvořit instanci třídy FileInfo. Konstruktor této třídy obsahuje parametr (string), reprezentující jméno souboru s úplnou nebo relativní cestou. Třídu FileInfo má smysl použít místo třídy File v případě, že s daným souborem chceme provést více operací. Oproti třídě File obsahuje třída FileInfo např. vlastnost Length, která poskytuje velikost souboru.

Adresáře ◦ Pro práci s adresáři slouží statická třída System.IO.Directory a nestatická System.IO.DirectoryInfo. ◦ Třída Directory obsahuje řadu veřejných statických metod parametr cesta v jednotlivých metodách může

představovat úplnou nebo relativní cestu. Kromě kořenového adresáře nemusí končit lomítkem.

Adresáře ◦ Třída Directory – nejdůležitější metody bool Exists(string path) Vrací true, pokud adresář path existuje.

DirectoryInfo CreateDirectory(string path) Vytvoří adresář path a vrací instanci třídy System.IO.DirectoryInfo.

void Delete(string path) Vymaže prázdný adresář path.

void Delete(string path, bool recursive) Je-li recursive rovno false, vymaže prázdný adresář path.

Jinak vymaže adresář path včetně podadresářů a všech souborů.

Adresáře ◦ Třída Directory – nejdůležitější metody void Move(string sourceDirName, string destDirName) Přesune adresář sourceDirName a jeho obsah na nové

místo destDirName. string[] GetFiles(string path) string[] GetFiles(string path, string searchPattern) Vrací pole jmen souborů, které obsahuje adresář path s

případným specifikováním masky pro hledání searchPattern, např. *.pdf.

Adresáře ◦ Třída Directory – nejdůležitější metody string[] GetDirectories(string path) string[] GetDirectories(string path, string searchPattern) Vrací pole jmen podadresářů, které obsahuje adresář path s

případným specifikováním masky pro hledání searchPattern.

string GetCurrentDirectory() Vrací jméno aktuálního pracovního adresáře aplikace.

void SetCurrentDirectory(string path) Nastaví aktuální pracovní adresář aplikace na path.

Adresáře ◦ Třída Directory – nejdůležitější metody string[] GetLogicalDrives() Vrací pole jmen logických disků definovaných na tomto

počítači, např. "C:\".

string GetDirectoryRoot(string path) Vrací jméno kořenového adresáře pro adresář path, např.

"C:\".

Adresáře ◦ Třída Directory – nejdůležitější metody DateTime GetCreationTime(string path) DateTime GetLastWriteTime(string path) DateTime GetLastAccessTime(string path) void SetCreationTime(string path, DateTime creationTime)

void SetLastAccessTime (string path, DateTime lastAccessTime)

void SetLastWriteTime(string path, DateTime lastWriteTime) Vrací nebo nastavuje datum a čas vytvoření, poslední

úpravy nebo přístupu k adresáři path.

Adresáře ◦ Třída DirectoryInfo

Vztah mezi třídou Directory a DirectoryInfo je obdobný jako mezi třídou File a FileInfo. Konstruktor této třídy obsahuje parametr typu string, reprezentující jméno adresáře s úplnou nebo relativní cestou stejně jako v metodách třídy Directory.

Cesty ◦ statická třída System.IO.Path ◦ Readonly datové složky AltDirectorySeparatorChar – alternativní oddělovač

adresářových úrovní.

DirectorySeparatorChar – oddělovač adresářových úrovní.

PathSeparator – oddělovač cest v proměnných prostředí (environment variables).

VolumeSeparatorChar – oddělovač za označením disku.

: ;

;

/ \

\ /

Adresáře ◦ Třída Path – nejdůležitější metody string ChangeExtension(string path, string extension) Změní příponu souboru path na extension a vrací nové

jméno souboru.

string Combine(string path1, string path2) Spojí dvě cesty v jednu, mezi něž vloží případně správný

oddělovač. Např. volání metody Path.Combine("c:\Dokumenty", "info.txt") vrací řetězec "c:\Dokumenty\info.txt".

Adresáře ◦ Třída Path – nejdůležitější metody string GetDirectoryName(string path) string GetExtension(string path) string GetFileNameWithoutExtension(string path) string GetPathRoot(string path) Metody vrací příslušnou část zadané cesty.

string GetFullPath(string path) Vrací úplnou cestu pro cestu path.

Příklad static string NajdiSoubor(string soubor) { string root = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()); return NajdiSouborVAdresari(soubor, root); } static string NajdiSouborVAdresari(string soubor, string adresar) { string oddelovac = Path.DirectorySeparatorChar.ToString(); // #1 string cesta = adresar.EndsWith(oddelovac) ? adresar + soubor : adresar + oddelovac + soubor; // #2 if (File.Exists(cesta)) return cesta; try { string[] adresare = Directory.GetDirectories(adresar); foreach (string adr in adresare) { cesta = NajdiSouborVAdresari(soubor, adr); if (cesta != null) return cesta; } } catch { return null; } return null; }

1

Předvádějící
Poznámky prezentace
Pr_directories Program hledá zadané jméno souboru bez cesty na aktuálním disku. Pokud jej nalezne, vypíše úplnou cestou k tomuto souboru. Hledané jméno souboru převezme z prvního parametru příkazového řádku. Metoda NajdiSoubor zjistí aktuální disk a zavolá metodu NajdiVAdresari, která hledá soubor v zadaném adresáři (nejprve v kořenovém adresáři). Pokud v zadaném adresáři soubor neexistuje, hledá jej v podadresářích rekurzivním voláním sebe sama. Jméno adresáře, ve kterém se má soubor hledat, je výsledkem volání metod GetDirectoryRoot a GetDirectories. Metoda GetDirectoryRoot vrací jméno kořenového adresáře s koncovým zpětným lomítkem \ a metoda GetDirectories vrací jména podadresářů bez koncových lomítek. Pokud adresář neobsahuje na konci zpětné lomítko, metoda jej doplní. Příkazy #1 a #2 lze nahradit příkazem string cesta = Path.Combine(adresar, soubor); Parametr příkazového řádku pro daný program lze v prostředí Visual Studio zadat pomocí menu Project | Properties, část Debug, pole Command Line Arguments.

Příklad

static void Main(string[] args) { if (args.Length == 0) Console.WriteLine("Nebylo zadáno jméno souboru"); else { string cesta = NajdiSoubor(args[0]); if (cesta == null) Console.WriteLine("Soubor {0} na tomto disku neexistuje", args[0]); else Console.WriteLine("Soubor {0} je v adresáři {1}", args[0], Path.GetDirectoryName(cesta)); } Console.ReadKey(); }

2

Předvádějící
Poznámky prezentace
Pr_directories Program hledá zadané jméno souboru bez cesty na aktuálním disku. Pokud jej nalezne, vypíše úplnou cestou k tomuto souboru. Hledané jméno souboru převezme z prvního parametru příkazového řádku. Metoda NajdiSoubor zjistí aktuální disk a zavolá metodu NajdiVAdresari, která hledá soubor v zadaném adresáři (nejprve v kořenovém adresáři). Pokud v zadaném adresáři soubor neexistuje, hledá jej v podadresářích rekurzivním voláním sebe sama. Jméno adresáře, ve kterém se má soubor hledat, je výsledkem volání metod GetDirectoryRoot a GetDirectories. Metoda GetDirectoryRoot vrací jméno kořenového adresáře s koncovým zpětným lomítkem \ a metoda GetDirectories vrací jména podadresářů bez koncových lomítek. Pokud adresář neobsahuje na konci zpětné lomítko, metoda jej doplní. Příkazy #1 a #2 lze nahradit příkazem string cesta = Path.Combine(adresar, soubor); Parametr příkazového řádku pro daný program lze v prostředí Visual Studio zadat pomocí menu Project | Properties, část Debug, pole Command Line Arguments.

Disky ◦ Třída System.IO.DriveInfo Konstruktor obsahuje parametr typu string, reprezentující

označení disku, např. "d", "d:" nebo "d:\". Většina informací o daném disku je dostupná pomocí

vlastností, poskytující např. celkovou kapacitu disku, velikost volného prostoru, typ disku (např. CD-ROM), formát disku (např. NTFS), zda je určen pouze pro čtení.

static DriveInfo[] GetDrives() Statická metoda vracející pole všech disků na počítači

Příklad static void VypisDiskInfo(DriveInfo di) { Console.WriteLine("Informace o disku"); Console.WriteLine("Označení: {0}", di.Name); Console.WriteLine("Typ: {0}", di.DriveType); if (di.IsReady) { Console.WriteLine("Jmenovka: {0}", di.VolumeLabel); Console.WriteLine("Kapacita: {0} GiB", (di.TotalSize / 1024f / 1024f / 1024f).ToString()); Console.WriteLine("Volný prostor: {0}", di.AvailableFreeSpace); Console.WriteLine("Formát: {0}", di.DriveFormat); } else { Console.WriteLine("Disk není připraven"); } } static void Main(string[] args) { Console.Write("Zadej označení disku: "); string disk = Console.ReadLine(); DriveInfo di = new DriveInfo(disk); VypisDiskInfo(di); Console.ReadKey(); }

1

Předvádějící
Poznámky prezentace
Pr_drivers Program vypíše informace o zadaném disku.

Vstupy a výstupy Čtení ze souboru a zápis do něj se provádí pomocí datových proudů. Jazyk C# umožňuje skládání datových proudů, které převzal z jazyka Java.

Datový proud představuje nástroj pro přenos dat ze zdroje ke spotřebiči. Zdrojem může být program, soubor, síťové spojení aj. Spotřebičem může být opět program, soubor aj. Datový proud se stará o formátování, vyrovnávací paměť aj.

Skládání datových proudů funguje takto. Data od zdroje jdou do jednoho datového proudu, který je nějakým způsobem upraví a předá je dalšímu proudu atd. až je poslední datový proud předá spotřebiči. První proud může např. dostávat z programu data v binární podobě, formátovat je a předávat dalšímu proudu, který se postará o jejich uložení do textového souboru.

Vstupy a výstupy ◦ Knihovna BCL nabízí celou řadu druhů datových proudů, např.: System.IO.FileStream – souborový proud. System.IO.MemoryStream – paměťový proud. System.Net.Sockets.NetworkStream – síťový proud. System.IO.BufferedStream – proud s vyrovnávací paměti. System.IO.Compression.DeflateStream – pro komprimaci a dekomprimaci. System.IO.Compression.GZipStream – pro komprimaci a dekomprimaci. System.Security.Cryptography.CryptoStream – kryptografický proud. System.IO.BinaryReader – pro čtení binárních dat. System.IO.BinaryWriter – pro zápis binárních dat. System.IO.TextReader – abstraktní třída určená pro čtení znaků. System.IO.TextWriter – abstraktní třída určená pro zápis znaků. System.IO.StreamReader – pro čtení znaků – je potomkem třídy TextReader. System.IO.StreamWriter – pro zápis znaků – je potomkem třídy TextWriter. System.IO.StringReader – pro čtení znaků z řetězce – je potomkem třídy

TextReader. System.IO.StringWriter – pro zápis znaků do řetězce – je potomkem třídy

TextWriter.

Vstupy a výstupy ◦ Třídy XxxStream jsou předkem abstraktní třídy System.IO.Stream. ◦ Třídy FileStream, MemoryStream a NetworkStream představují tzv.

podkladové proudy pro ostatní uvedené třídy, které mají první parametr konstruktoru typu Stream.

◦ Třída Stream nabízí mj. složky: Vlastnost Position – poskytuje nebo nastavuje pozici ukazatele v proudu. Vlastnosti CanRead, CanWrite, CanSeek – poskytují informace, jaké typy operací

lze s proudem provádět (např. síťový proud neumožňuje přesun ukazatele v proudu).

◦ Metoda Seek – přesune ukazatel v proudu. ◦ Metoda Read – přečte pole bytů z proudu. ◦ Metoda Write – zapíše pole bytů do proudu. ◦ Metoda ReadByte – přečte jeden byte z proudu. ◦ Metoda WriteByte – zapíše jeden byte do proudu. ◦ Metoda Close – zavře proud – volá metodu Dispose.

Čtení a zápis binárních dat ◦ Lze provádět pouze pro základní datové typy ◦ Pro čtení/zápis z/do souboru se používá třída FileStream, která pracuje

s typem byte[] – to není příliš pohodlné, proto se často kombinuje s BinaryReader a BinaryWriter.

◦ Instanci FileStream lze získat kromě voláním konstruktoru také voláním metody z třídy File (Create, Open)

◦ Třída FileStream implementuje rozhraní IDisposable, jenž využívá příkaz using. Metoda Dispose zavře případně otevřený soubor. Pokud uživatel nevolá metodu Close sám, měl by pracovat s instancí třídy FileStream v příkazu using, jinak by po ukončení práce se souborem zůstal soubor stále otevřený, dokud by automatická správa paměti instanci této třídy nezrušila.

◦ Rozhraní IDisposable podporují i ostatní datové proudy. Metoda Dispose nějakého proudu uvolní daný proud a uvolní i proudy, se kterými je proud spojen. Stačí tedy zavolat metodu Dispose resp. použít příkaz using na některý z proudů, které jsou spojeny.

Čtení a zápis binárních dat ◦ Třídy BinaryReader a BinaryWriter představují vrchní proudy - mají

konstruktor s jedním parametrem typu Stream, do něhož lze předat instanci třídy FileStream.

◦ Třída BinaryWriter obsahuje mj. tyto metody: Seek – přesune ukazatel v proudu. Write – metody jsou přetíženy pro jednotlivé základní datové typy a typy string,

byte[] a char[] – zapíší hodnotu daného typu do proudu.

◦ Třída BinaryReader obsahuje mj. tyto metody: ReadByte, ReadInt32 aj. pro jednotlivé základní datové typy a metody

ReadString, ReadBytes a ReadChars pro typy string, byte[] a char[] – přečtou hodnotu daného typu z proudu, kterou vrací. Pokud dojdou na konec proudu, vyvolají výjimku EndOfStreamException.

PeekChar – vrací následující znak v proudu, ale neposune ukazatel v proudu.

Čtení a zápis binárních dat – příklad

public static void Uloz(string jmeno) { FileStream fs = new FileStream(jmeno, FileMode.Create); using (BinaryWriter bw = new BinaryWriter(fs)) { Random r = new Random(); for (int i = 0; i < 20; i++) { int j = r.Next(100); bw.Write(j); } } } public static void Nacti(string jmeno) { FileStream fs = new FileStream(jmeno, FileMode.Open); try { using (BinaryReader bw = new BinaryReader(fs)) { do { int i = bw.ReadInt32(); Console.WriteLine(i); } while (true); } } catch (EndOfStreamException) { } // OK }

1

Předvádějící
Poznámky prezentace
Pr_bin_files Program ukládá náhodná celá čísla do binárního souboru a potom je čte. Pokud se při čtení dojde na konec souboru, vznikne výjimka typu EndOfStreamException, která se zachytí, ale není ji třeba ošetřovat. Metoda Uloz by mohla být zapsána také takto: public static void Uloz(string jmeno) { FileStream fs = new FileStream(jmeno, FileMode.Create); BinaryWriter bw = new BinaryWriter(fs); try { Random r = new Random(); for (int i = 0; i < 20; i++) { int j = r.Next(100); bw.Write(j); } } finally { bw.Close(); // fs.Close(); // fs.Dispose(); } } Na konci metody se musí zavolat metoda Dispose nebo Close alespoň pro jeden z datových proudů, jinak při pokusu o čtení dat vznikne výjimka.

Čtení a zápis binárních dat – příklad

static void Main(string[] args){ try { Uloz("data.bin"); Nacti("data.bin"); } catch (Exception e){ Console.WriteLine("Chyba: " + e.Message) } Console.ReadKey(); }

2

public static void Uloz2(string jmeno) { FileStream fs = new FileStream(jmeno, FileMode.Create); BinaryWriter bw = new BinaryWriter(fs); try { Random r = new Random(); for (int i = 0; i < 20; i++) { int j = r.Next(100); bw.Write(j); } } finally { bw.Close(); // fs.Close(); // fs.Dispose(); } }

uloz2

Předvádějící
Poznámky prezentace
Pr_bin_files Program ukládá náhodná celá čísla do binárního souboru a potom je čte. Pokud se při čtení dojde na konec souboru, vznikne výjimka typu EndOfStreamException, která se zachytí, ale není ji třeba ošetřovat. Metoda Uloz by mohla být zapsána také takto: public static void Uloz(string jmeno) { FileStream fs = new FileStream(jmeno, FileMode.Create); BinaryWriter bw = new BinaryWriter(fs); try { Random r = new Random(); for (int i = 0; i < 20; i++) { int j = r.Next(100); bw.Write(j); } } finally { bw.Close(); // fs.Close(); // fs.Dispose(); } } Na konci metody se musí zavolat metoda Dispose nebo Close alespoň pro jeden z datových proudů, jinak při pokusu o čtení dat vznikne výjimka.

Čtení a zápis textových dat

◦ Pro čtení a zápis textových dat se používají většinou třídy StreamReader, StreamWriter, které jsou odvozené od tříd TextReader a TextWriter.

◦ Třída StreamWriter, jakož i třída TextWriter obsahuje mj. metody Write a WriteLine sloužící pro zápis hodnot základních datových typů a typu string a char[] a pro zápis formátovaného řetězce. Metody se používají stejně jako metody Write a WriteLine

třídy Console.

Čtení a zápis textových dat ◦ Třída StreamReader (TextReader) obsahuje mj. tyto složky: Metoda int Read() – přečte následující znak z proudu. Pokud již v

proudu žádný znak není, vrací –1 a výjimku nevyvolá.

Metoda int Read (char[] buffer, int index, int count) – přečte count znaků z proudu a uloží je do pole buffer počínaje indexem index. Vrací počet přečtených znaků. Pokud se dojde na konec proudu, výjimku nevyvolá.

Metoda string ReadLine() – přečte jeden řádek z proudu. Pokud již v proudu není další řádek, vrací null a výjimku nevyvolá.

Vlastnost EndOfStream – poskytuje hodnotu true, pokud bylo dosaženo konce proudu.

◦ Třída neobsahuje specializované metody pro čtení základních

datových typů.

Čtení a zápis textových dat – příklad static void Vypis(TextWriter tw, int[,] matice) { for (int i = 0; i < matice.GetLength(0); i++) { for (int j = 0; j < matice.GetLength(1); j++) { tw.Write("{0,5}", matice[i, j]); } tw.WriteLine(); } } static void Uloz(string jmeno, int[,] matice) { using (StreamWriter sw = new StreamWriter(File.Create(jmeno))) { sw.WriteLine("{0}, {1}", matice.GetLength(0), matice.GetLength(1)); Vypis(sw, matice); } }

1

Předvádějící
Poznámky prezentace
Pr_txt_file Program uloží matici do textového souboru, potom ji načte a vypíše na obrazovku. Metoda Vypis slouží pro zápis matice do proudu typu TextWriter. Každý řádek matice zapíše na samostatný řádek. Každý prvek matice je zarovnán napravo prostoru šířky 5 znaků. Metoda je volána jak pro zápis matice do textového souboru, tak pro výpis matice na obrazovku. Skutečným parametrem je v tomto případě Console.Out, což je vlastnost, poskytující výstupní datový proud typu TextWriter. Vlastnost In třídy Console poskytuje zase vstupní datový proud typu TextReader. Na první řádek textového souboru se zapisuje počet řádků a sloupců matice oddělených čárkou a mezerou. Při čtení matice z textového souboru se využívá metoda ReadLine třídy StreamReader. Pro načtený řetězec znaků, reprezentující jeden řádek souboru, se volá metoda Split, která vrací dílčí řetězce rozdělené zadanými oddělovači. Při čtení prvního řádku se použije oddělovač čárka. Při čtení řádků matice se použijí oddělovače mezera a tabulátor (tabulátor uvedený textový soubor neobsahuje – je zde uveden jako příklad). K rozdělení řádku matice na jednotlivé prvky se volá metoda Split s parametrem StringSplitOptions.RemoveEmptyEntries, který zajišťuje vynechání prázdných řetězců.

Čtení a zápis textových dat – příklad static void Nacti(string jmeno, out int[,] matice) { using (StreamReader sr = new StreamReader(File.OpenRead(jmeno))) { string radek = sr.ReadLine(); string[] texty = radek.Split(','); int pocRadku = int.Parse(texty[0]); int pocSloupcu = int.Parse(texty[1]); matice = new int[pocRadku, pocSloupcu]; char[] oddelovace = new char[] { ' ', '\t' }; for (int i = 0; i < pocRadku; i++) { radek = sr.ReadLine(); texty = radek.Split(oddelovace, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < pocSloupcu; j++) { matice[i, j] = int.Parse(texty[j]); } } } }

2

Předvádějící
Poznámky prezentace
Pr_txt_file Program uloží matici do textového souboru, potom ji načte a vypíše na obrazovku. Metoda Vypis slouží pro zápis matice do proudu typu TextWriter. Každý řádek matice zapíše na samostatný řádek. Každý prvek matice je zarovnán napravo prostoru šířky 5 znaků. Metoda je volána jak pro zápis matice do textového souboru, tak pro výpis matice na obrazovku. Skutečným parametrem je v tomto případě Console.Out, což je vlastnost, poskytující výstupní datový proud typu TextWriter. Vlastnost In třídy Console poskytuje zase vstupní datový proud typu TextReader. Na první řádek textového souboru se zapisuje počet řádků a sloupců matice oddělených čárkou a mezerou. Při čtení matice z textového souboru se využívá metoda ReadLine třídy StreamReader. Pro načtený řetězec znaků, reprezentující jeden řádek souboru, se volá metoda Split, která vrací dílčí řetězce rozdělené zadanými oddělovači. Při čtení prvního řádku se použije oddělovač čárka. Při čtení řádků matice se použijí oddělovače mezera a tabulátor (tabulátor uvedený textový soubor neobsahuje – je zde uveden jako příklad). K rozdělení řádku matice na jednotlivé prvky se volá metoda Split s parametrem StringSplitOptions.RemoveEmptyEntries, který zajišťuje vynechání prázdných řetězců.

Čtení a zápis textových dat – příklad static void Main(string[] args) { try { int[,] matice = new int[5, 6]; for (int i = 0; i < matice.GetLength(0); i++) { for (int j = 0; j < matice.GetLength(1); j++) { matice[i, j] = i + j; } } Uloz("data.txt", matice); Nacti("data.txt", out matice); Vypis(Console.Out, matice); } catch (Exception e) { Console.WriteLine("Chyba: " + e.Message); } Console.ReadKey(); }

3

Předvádějící
Poznámky prezentace
Pr_txt_file Program uloží matici do textového souboru, potom ji načte a vypíše na obrazovku. Metoda Vypis slouží pro zápis matice do proudu typu TextWriter. Každý řádek matice zapíše na samostatný řádek. Každý prvek matice je zarovnán napravo prostoru šířky 5 znaků. Metoda je volána jak pro zápis matice do textového souboru, tak pro výpis matice na obrazovku. Skutečným parametrem je v tomto případě Console.Out, což je vlastnost, poskytující výstupní datový proud typu TextWriter. Vlastnost In třídy Console poskytuje zase vstupní datový proud typu TextReader. Na první řádek textového souboru se zapisuje počet řádků a sloupců matice oddělených čárkou a mezerou. Při čtení matice z textového souboru se využívá metoda ReadLine třídy StreamReader. Pro načtený řetězec znaků, reprezentující jeden řádek souboru, se volá metoda Split, která vrací dílčí řetězce rozdělené zadanými oddělovači. Při čtení prvního řádku se použije oddělovač čárka. Při čtení řádků matice se použijí oddělovače mezera a tabulátor (tabulátor uvedený textový soubor neobsahuje – je zde uveden jako příklad). K rozdělení řádku matice na jednotlivé prvky se volá metoda Split s parametrem StringSplitOptions.RemoveEmptyEntries, který zajišťuje vynechání prázdných řetězců.

Kódování textů

◦ Texty (řetězce nebo znaky) v jazyce C# jsou v paměti uloženy v kódování označovaném běžně Unicode (kódová stránka 1200).

◦ Texty vypisované na obrazovku konzolové aplikace se implicitně převádí do kódování Latin 2 (kódová stránka 852).

◦ Texty zapisované do datového proudu se implicitně převádí do kódování UTF-8 (kódová stránka 65001). Při čtení se provádí opačný převod.

Kódování textů ◦ UTF je zkratka slov „Unicode Transformation Format“ -

určuje způsob zakódování Unicode kódů (znaků – 16bitových kódů) bez ztráty informací. Existují následující typy: UTF-7 – zastaralý způsob kódování, UTF-8 – převádí Unicode kód na posloupnost jednoho až čtyř bytů, UTF-16 – převádí Unicode kód na posloupnost jednoho až dvou 16-

bitových celých čísel, UTF-32 – převádí Unicode kód na jedno 32-bitové celé číslo.

◦ Kódování UTF-16 a UTF-32 mají dva podtypy: little-endian byte order – např. znak A '\u0041' je zapsán

v UTF-16 ve tvaru 00 41, big-endian byte order – např. znak A '\u0041' je zapsán

v UTF-16 ve tvaru 41 00.

Kódování textů Běžné označení Unicode kódování je kódování UTF-16 little-endian byte order. ◦ Jeden Unicode kód umožňuje uchovat 65 535

znaků. Nestačí pro uchování všech znaků jazyků světa. Tyto a další znaky lze však také uchovat, a to jako

dvojici znaků – základní znak a doprovodný znak.

o (malé o s ogonkem) lze zapsat jako dvojici znaků "\u006F\u0328"

Předvádějící
Poznámky prezentace
Např. čínský jazyk vyžaduje více než 80 000 znaků. Tyto a další znaky lze však také uchovat, a to jako dvojici znaků – základní znak a doprovodný znak. Doprovodné znaky jsou definovány v rozsahu '\u0328' až '\u0345' (podle [2]). Např. znak o (malé o s ogonkem) lze zapsat jako dvojici znaků "\u006F\u0328".

Kódování textů ◦ Typ kódování textů lze zadat v parametru

konstruktoru datových proudů BinaryReader, BinaryWriter, StreamReader a StreamWriter. Parametr je typu System.Text.Encoding, což je

abstraktní třída, která je předkem tříd, jež představují jednotlivá kódování.

◦ Většina potřebných kódování je k dispozici

pomocí statických složek třídy Encoding Vlastnosti Default, Unicode, UTF8, UTF32 metoda GetEncoding(int codepage) – vrací

kódování pro zadané číslo kódové stránky

Kódování – příklad

static void UlozText(string jmeno, string text, Encoding encoding) { using (StreamWriter sw = new StreamWriter(File.Create(jmeno), encoding)) { sw.WriteLine(text); } } static void Main(string[] args) { string text = @"Nějaký český text Příliš žluťoučký kůň úpěl ďábelské ódy"; UlozText("data.txt", text, Encoding.Default); }

1

Předvádějící
Poznámky prezentace
Pr_kodovani_souboru Program uloží zadaný text do textového souboru v implicitním kódování ANSI, což v českých Windows bude kódová stránka 1250.

Paměťové datové proudy ◦ Oproti C++ jsou paměťové proudy v C#

orientovány na pole bytů a ne na řetězce znaků. ◦ Třída MemoryStream MemoryStream() – vytvoří prázdný proud MemoryStream(byte[] buffer) – vytvoří proud

svázaný s externím polem bytů buffer. Pole bytů nelze později zvětšit.

MemoryStream(int capacity) – vytvoří proud s vnitřním polem bytů velikosti capacity. Pole bytů lze později zvětšit.

Paměťové datové proudy Pro čtení a zápis z/do paměťového proudu lze využít metody třídy Stream, ale zpravidla se využívají třídy BinaryReader, BinaryWriter, StreamReader a StreamWriter. Třída MemoryStream mj. obsahuje metodu byte[] ToArray(), která vrací kopii vnitřního pole bytů paměťového proudu.

Paměťové proudy - příklad

static void Main(string[] args) { string text = "Příliš žluťoučký kůň."; MemoryStream ms = new MemoryStream(); using (StreamWriter sw = new StreamWriter(ms, Encoding.Default)) { sw.Write(text); } byte[] buffer = ms.ToArray(); buffer = Encoding.Convert(Encoding.Default, Encoding.Unicode, buffer); string text2 = Encoding.Unicode.GetString(buffer); Console.WriteLine(text2); Console.ReadKey(); }

1

Předvádějící
Poznámky prezentace
Pr_memorystream Program provádí konverzi řetězce znaků z jednoho kódování do jiného. Text určený ke konverzi se zapíše do pole bytů pomocí paměťového datového proudu v zadaném kódování. Pole bytů se převede z jednoho kódování na jiné pomocí statické metody Convert třídy Encoding: byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, byte[] bytes) Metoda Convert převede pole bytů bytes z kódování srcEncoding na kódování dstEncoding a vrací převedené pole bytů. Převod pole bytů na řetězec provádí statická metoda GetString příslušné třídy kódování. V příkladu je to třída, kterou poskytuje vlastnost Encoding.Unicode.

Paměťové datové proudy

Pro převod řetězce znaků na pole bytů kódové stránky odpovídající danému potomkovi třídy Encoding slouží virtuální metoda GetBytes třídy Encoding: virtual byte[] GetBytes(string s) Např. převod řetězce znaků s na pole bytů kódové stránky 1250 lze provést příkazem: byte[] b = Encoding.Default.GetBytes(s);

Paměťové proudy - příklad class Osoba

{ public const int Delka = 100; public int cislo; public string jmeno; public Osoba() { } public Osoba(int cislo, string jmeno) { this.cislo = cislo; this.jmeno = jmeno; } public void NactiPevnaDelka(BinaryReader br) { byte[] buffer = br.ReadBytes(Delka); MemoryStream ms = new MemoryStream(buffer); using (BinaryReader br2 = new BinaryReader(ms)) { cislo = br2.ReadInt32(); jmeno = br2.ReadString(); } }

1

Předvádějící
Poznámky prezentace
Pr_memorystream_binary Příklad demonstruje uložení pole tříd Osoba do binárního souboru a jeho načtení. Záznam osoby v souboru má pevnou délku, což umožňuje případné načtení jen konkrétního záznamu v souboru. Při ukládání údajů osoby se vytvoří pole bytů odpovídající délce záznamu osoby, které se spojí s paměťovým proudem. Do paměťového proudu se binárně uloží údaje osoby. Potom se pole bytů uloží do binárního souboru. Při čtení údajů osoby se z binárního souboru načte pole bytů odpovídající délce záznamu osoby. Toto pole se spojí se paměťovým proudem, ze kterého se binárně načtou údaje osoby.

Paměťové proudy - příklad public void UlozPevnaDelka(BinaryWriter bw)

{ byte[] buffer = new byte[Delka]; MemoryStream ms = new MemoryStream(buffer); using (BinaryWriter bw2 = new BinaryWriter(ms)) { bw2.Write(cislo); bw2.Write(jmeno); } bw.Write(buffer); } } class Program { static void UlozPevnaDelka(string jmeno, Osoba[] osoby) { using (BinaryWriter bw = new BinaryWriter(File.Create(jmeno))) { foreach (Osoba osoba in osoby) { osoba.UlozPevnaDelka(bw); } } }

2

Předvádějící
Poznámky prezentace
Pr_memorystream_binary Příklad demonstruje uložení pole tříd Osoba do binárního souboru a jeho načtení. Záznam osoby v souboru má pevnou délku, což umožňuje případné načtení jen konkrétního záznamu v souboru. Při ukládání údajů osoby se vytvoří pole bytů odpovídající délce záznamu osoby, které se spojí s paměťovým proudem. Do paměťového proudu se binárně uloží údaje osoby. Potom se pole bytů uloží do binárního souboru. Při čtení údajů osoby se z binárního souboru načte pole bytů odpovídající délce záznamu osoby. Toto pole se spojí se paměťovým proudem, ze kterého se binárně načtou údaje osoby.

Paměťové proudy - příklad static void NactiPevnaDelka(string jmeno, out Osoba[] osoby)

{ FileStream fs = File.OpenRead(jmeno); int pocet = (int)(fs.Length / Osoba.Delka); osoby = new Osoba[pocet]; using (BinaryReader br = new BinaryReader(fs)) { for (int i = 0; i < pocet; i++) { osoby[i] = new Osoba(); osoby[i].NactiPevnaDelka(br); } } } static void Main(string[] args) { Osoba[] osoby = new Osoba[3] { new Osoba(10, "Čejka"), new Osoba(20, "Dvořák"), new Osoba(30, "Mojžíš") }; try { UlozPevnaDelka("data.bin", osoby); NactiPevnaDelka("data.bin", out osoby); foreach (Osoba osoba in osoby) { Console.WriteLine("{0} {1}", osoba.cislo, osoba.jmeno); } } catch (Exception e) { Console.WriteLine("Chyba: " + e.Message); } Console.ReadKey(); } }

3

Předvádějící
Poznámky prezentace
Pr_memorystream_binary Příklad demonstruje uložení pole tříd Osoba do binárního souboru a jeho načtení. Záznam osoby v souboru má pevnou délku, což umožňuje případné načtení jen konkrétního záznamu v souboru. Při ukládání údajů osoby se vytvoří pole bytů odpovídající délce záznamu osoby, které se spojí s paměťovým proudem. Do paměťového proudu se binárně uloží údaje osoby. Potom se pole bytů uloží do binárního souboru. Při čtení údajů osoby se z binárního souboru načte pole bytů odpovídající délce záznamu osoby. Toto pole se spojí se paměťovým proudem, ze kterého se binárně načtou údaje osoby.

mechanizmus umožňující uložit do datového proudu jakýkoli objekt včetně vazeb na další objekty

ukládá se tzv. objektový graf ◦ všechny související objekty ◦ informace o vazbách mezi objekty

Opačným procesem je deserializace ◦ z datového proudu se načte a vytvoří objekt včetně

všech objektů, na který se tento odkazuje

Serializaci lze provést v těchto formátech: ◦ binární, ◦ SOAP (Simple Object Access Protocol) – protokol

založený na XML určený pro výměnu informací na webu, ◦ XML.

binární nebo SOAP serializace ◦ Ukládané objekty musí splňovat: Třída (struktura) jejíž instance se má serializovat musí

být deklarována s atributem Serializable.

Datové složky, které se nemají serializovat, se deklarují s atributem Nonserialized. Při načtení objektu z datového proudu se provede jejich inicializace v závislosti na jejich typu: jsou-li hodnotového typu, volá se pro ně implicitní

konstruktor, jsou-li referenčního typu, mají hodnotu null.

binární nebo SOAP serializace ◦ Ukládané objekty musí splňovat: Datové složky, které se mají serializovat, musí být

typu, který je deklarován s atributem Serializable, jinak při serializaci vznikne výjimka typu System.Runtime.Serialization.SerializationException. To se týká i datových složek objektu, na který se datová složka této třídy přímo nebo nepřímo odkazuje.

binární nebo SOAP serializace ◦ Datové složky se serializují bez ohledu na jejich

přístupová práva. Vlastnosti se neserializují. ◦ S atributem Serializable jsou deklarovány

všechny základní datové typy, typ string i třídy kolekcí Array, ArrayList aj.

binární nebo SOAP serializace ◦ Pro serializaci slouží následující třídy: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter – serializace v binárním formátu,

◦ System.Runtime.Serialization.Formatters.Soap.SoapFormatter – serializace ve formátu SOAP. Třída je součástí sestavení System.Runtime.Serialization.Formatters.Soap, které se v prostředí Visual Studio musí přidat do části References (záložka .NET) daného projektu.

binární nebo SOAP serializace ◦ Běžně se používá konstruktor bez parametrů. Z

metod se používají následující dvě: void Serialize(Stream serializationStream, object graph) – zapíše objekt graph do datového proudu serializationStream.

object Deserialize(Stream serializationStream) – vrací objekt, který načte z datového proudu serializationStream.

binární nebo SOAP serializace ◦ Obě třídy formátovačů implementují rozhraní IFormatter¸ které obsahuje metody Serialize a Deserialize. Pokud tedy chceme provádět serializaci nějakého objektu nezávislého na formátu, lze pracovat s tímto rozhraním. ◦ Do jednoho datového proudu lze serializovat i více

objektů samostatným voláním metod Serialize a později je deserializovat metodami Deserialize ve stejném pořadí, v jakém byly serializovány.

binární nebo SOAP serializace ◦ Pokud některá složka objektu, která se má

serializovat, se metodou Deserialize nenačte, chování závisí na zvoleném formátovači: Pro binární formát výjimka nevznikne a taková složka

se inicializuje stejnou hodnotou jako složka, která se nemá serializovat.

Pro formát SOAP se vyvolá výjimka typu SerializationException. Pokud je však složka deklarována s atributem OptionalField, inicializuje se stejnou hodnotou jako složka, která se nemá serializovat a výjimka nevznikne.

Příklad ◦ Příklad demonstruje serializaci třídy C, která

obsahuje pole tříd B. Třída B obsahuje složky a, a2 typu struktury A a složku y typu int. Složka a2 se nebude serializovat. Jak třídy B a C, tak i struktura A musí být deklarovány s atributem Serializable, jinak při serializaci vznikne výjimka.

Příklad

[Serializable] struct A { public int x; public A(int x) { this.x = x; } } [Serializable] class B { A a; [NonSerialized] A a2 = new A(-1); int y; public B(int x, int y) { a = new A(x); this.y = y; } public override string ToString() { return "a.x = " + a.x + ", a2.x = " + a2.x + ", y = " + y; } }

1

Předvádějící
Poznámky prezentace
Pr_serializace Příklad demonstruje serializaci třídy C, která obsahuje pole tříd B. Třída B obsahuje složky a, a2 typu struktury A a složku y typu int. Složka a2 se nebude serializovat. Jak třídy B a C, tak i struktura A musí být deklarovány s atributem Serializable, jinak při serializaci vznikne výjimka.

Příklad

[Serializable] class C { B[] b; public C(B[] b) { this.b = b; } public void Vypis() { foreach (B x in b) { Console.WriteLine(x.ToString()); } } }

2

Předvádějící
Poznámky prezentace
Pr_serializace Příklad demonstruje serializaci třídy C, která obsahuje pole tříd B. Třída B obsahuje složky a, a2 typu struktury A a složku y typu int. Složka a2 se nebude serializovat. Jak třídy B a C, tak i struktura A musí být deklarovány s atributem Serializable, jinak při serializaci vznikne výjimka.

Příklad class Program { static void Main(string[] args) { C c = new C(new B[] { new B(10, 20), new B(30, 40) }); //BinaryFormatter bf = new BinaryFormatter(); SoapFormatter bf = new SoapFormatter(); using (FileStream fs = new FileStream("data.bin", FileMode.Create)) { bf.Serialize(fs, c); } c.Vypis(); c = null; using (FileStream fs = new FileStream("data.bin", FileMode.Open)) { c = (C)bf.Deserialize(fs); } Console.WriteLine("Po načtení objektu ze souboru"); c.Vypis(); Console.ReadLine(); } }

3

Předvádějící
Poznámky prezentace
Pr_serializace Příklad demonstruje serializaci třídy C, která obsahuje pole tříd B. Třída B obsahuje složky a, a2 typu struktury A a složku y typu int. Složka a2 se nebude serializovat. Jak třídy B a C, tak i struktura A musí být deklarovány s atributem Serializable, jinak při serializaci vznikne výjimka.

Přizpůsobení serializace ◦ Proces serializace lze přizpůsobit např. za účelem určité

transformace hodnot složek některé třídy, která je součástí objektového grafu. K tomuto účelu slouží rozhraní ISerializable, které musí třída, jež se má serializovat specifickým způsobem, implementovat. Třída musí i tak být deklarována s atributem Serializable.

◦ Rozhraní ISerializable je deklarováno následovně: public interface ISerializable { void GetObjectData(SerializationInfo info, StreamingContext context); }

Přizpůsobení serializace ◦ Metodu GetObjectData volá automaticky daný

formátovač před uložením dané třídy do datového proudu. Třída, implementující uvedené rozhraní, musí naplnit parametr info kolekcí dvojic název/hodnota. název zpravidla reprezentuje název datové složky,

která se má serializovat hodnota reprezentuje její hodnotu. ◦ Třída SerializationInfo obsahuje pro účely

serializace přetížené metody AddValue, které mají tvar: void AddValue(string name, typ value)

základní datový typ

Přizpůsobení serializace ◦ Pokud se má do kolekce přidat dílčí objekt

objektového grafu (včetně objektů, na které odkazuje), na který se odkazuje příslušná datové složka třídy, lze volat metodu

void AddValue(string name, object value)

Přizpůsobení serializace ◦ Typ StreamingContext je struktura obsahující dvě

vlastnosti: Context – poskytuje objekt typu object, který může

obsahovat libovolné dodatečné informace. State – poskytuje příznaky, udávající o jaký typ

serializace se jedná, např. serializace do souboru, mezi aplikačními doménami, mezi počítači apod.

◦ Obě tyto vlastnosti jsou inicializovány

konstruktorem této třídy. Instanci této třídy lze předat konstruktoru příslušného formátovače.

Přizpůsobení serializace ◦ Třídy BinaryFormatter a SoapFormatter obsahují

k tomuto účelu konstruktor se dvěma parametry: XxxFormatter(ISurrogateSelector selector, StreamingContext context)

◦ Parametr selector se používá při serializaci po síti pomocí technologie .NET Remoting (podrobnosti viz nápověda). Pro jinou serializaci může být null.

Přizpůsobení serializace ◦ Kromě složek rozhraní ISerializable musí třída

obsahovat chráněný konstruktor ve tvaru: protected NejakaTrida(SerializationInfo info, StreamingContext context)

◦ Je-li třída zapečetěná, může být konstruktor soukromý.

Přizpůsobení serializace ◦ Tento konstruktor volá daný formátovač po načtení

hodnot dané třídy z datového proudu. Pro získání prvku z kolekce název/hodnota z třídy SerializationInfo se používají metody ve tvaru: typ GetTyp(string name) ◦ typ a Typ reprezentují jednotlivé základní datové

typy. ◦ Pro deserializaci dílčího objektu objektového grafu

lze volat metodu object GetValue(string name, Type type) ◦ Do parametru type se musí předat informace o

skutečném typu dílčího objektu, který metoda vrací.

Přizpůsobení serializace - příklad ◦ Program provádí serializaci a deserializaci třídy Student.

Tato třída obsahuje datovou složku fakulta výčtového typu a obor typu třída StudijniObor. Implicitně se hodnota výčtového typu serializuje ve formátu SOAP v podobě názvu výčtové konstanty. Pokud se při deserializaci načte název neodpovídající žádné z výčtových konstant, vyvolá se výjimka. Aby k tomu nedošlo, datová složka fakulta se serializuje jako celé číslo. Při deserializaci se kontroluje, zda celé číslo odpovídá některé z výčtových konstant. Pokud ano, převede se na odpovídající výčtovou konstantu a uloží se do datové složky fakulta. Pokud ne, do datové složky fakulta se uloží implicitní výčtová konstanta.

Přizpůsobení serializace – příklad

public enum Fakulta { DFJP, FES, FCHT, FEI }; [Serializable] public class StudijniObor { string nazev; int cislo; public StudijniObor(string nazev, int cislo) { this.nazev = nazev; this.cislo = cislo; } public override string ToString() { return "obor: " + nazev + ", " + cislo; } }

1

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni Program provádí serializaci a deserializaci třídy Student. Tato třída obsahuje datovou složku fakulta výčtového typu a obor typu třída StudijniObor. Implicitně se hodnota výčtového typu serializuje ve formátu SOAP v podobě názvu výčtové konstanty. Pokud se při deserializaci načte název neodpovídající žádné z výčtových konstant, vyvolá se výjimka. Aby k tomu nedošlo, datová složka fakulta se serializuje jako celé číslo. Při deserializaci se kontroluje, zda celé číslo odpovídá některé z výčtových konstant. Pokud ano, převede se na odpovídající výčtovou konstantu a uloží se do datové složky fakulta. Pokud ne, do datové složky fakulta se uloží implicitní výčtová konstanta.

Přizpůsobení serializace – příklad

[Serializable] public class Student : ISerializable { private Fakulta fakulta; private StudijniObor obor; public Student(Fakulta fakulta, StudijniObor obor){ this.fakulta = fakulta; this.obor = obor; } protected Student(SerializationInfo info, StreamingContext context){ obor = (StudijniObor)info.GetValue("Obor", typeof(StudijniObor)); int f = info.GetInt32("Fakulta"); foreach (int item in Enum.GetValues(typeof(Fakulta))) { if (item == f) { fakulta = (Fakulta)f; return; } } fakulta = Fakulta.DFJP; Console.WriteLine("Byla nastavena implictní fakulta"); }

public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Obor", obor); info.AddValue("Fakulta", (int)fakulta); } public void Vypis() { Console.WriteLine("fakulta: " + fakulta + ", " + obor); } }

2 3

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni Program provádí serializaci a deserializaci třídy Student. Tato třída obsahuje datovou složku fakulta výčtového typu a obor typu třída StudijniObor. Implicitně se hodnota výčtového typu serializuje ve formátu SOAP v podobě názvu výčtové konstanty. Pokud se při deserializaci načte název neodpovídající žádné z výčtových konstant, vyvolá se výjimka. Aby k tomu nedošlo, datová složka fakulta se serializuje jako celé číslo. Při deserializaci se kontroluje, zda celé číslo odpovídá některé z výčtových konstant. Pokud ano, převede se na odpovídající výčtovou konstantu a uloží se do datové složky fakulta. Pokud ne, do datové složky fakulta se uloží implicitní výčtová konstanta.

Přizpůsobení serializace – příklad class Program

{ static void Serializuj(IFormatter f, Student userConfig) { using (FileStream fs = File.Create("data.bin")) { f.Serialize(fs, userConfig); } } static Student Deserializuj(IFormatter f) { using (FileStream fs = File.OpenRead("data.bin")) { return (Student)f.Deserialize(fs); } } static void Main(string[] args) { Student student = new Student(Fakulta.FEI, new StudijniObor("IT", 10)); //BinaryFormatter f = new BinaryFormatter(); SoapFormatter f = new SoapFormatter(); Serializuj(f, student); student.Vypis(); student = Deserializuj(f); Console.WriteLine("\nPo deserializaci\n"); student.Vypis(); Console.ReadKey(); } }

4

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni Program provádí serializaci a deserializaci třídy Student. Tato třída obsahuje datovou složku fakulta výčtového typu a obor typu třída StudijniObor. Implicitně se hodnota výčtového typu serializuje ve formátu SOAP v podobě názvu výčtové konstanty. Pokud se při deserializaci načte název neodpovídající žádné z výčtových konstant, vyvolá se výjimka. Aby k tomu nedošlo, datová složka fakulta se serializuje jako celé číslo. Při deserializaci se kontroluje, zda celé číslo odpovídá některé z výčtových konstant. Pokud ano, převede se na odpovídající výčtovou konstantu a uloží se do datové složky fakulta. Pokud ne, do datové složky fakulta se uloží implicitní výčtová konstanta.

Přizpůsobení serializace ◦ Proces serializace lze od verze .NET 2.0 také

ovlivňovat metodami, které jsou deklarovány s atributy: OnDeserialized – metoda s tímto atributem se zavolá

bezprostředně poté, co byl objekt deserializován. OnDeserializing – metoda s tímto atributem se

zavolá bezprostředně před deserializací objektu. OnSerialized – metoda s tímto atributem se zavolá

bezprostředně poté, co byl objekt serializován. OnSerializing – metoda s tímto atributem se zavolá

bezprostředně před serializací objektu.

Metody s uvedenými atributy musí mít jeden parametr typu StreamingContext a vracet void.

Přizpůsobení serializace – příklad ◦ Program serializuje pole tříd Bod. Třída Bod má

metodu OnSerialized, která je volána po serializaci bodu. Tato metoda vypíše na obrazovku pořadové číslo bodu, který byl serializován a jeho údaje. ◦ Pořadové číslo bodu je uchováno ve vlastnosti Pocet třídy Pocitadlo, jejíž instance poskytuje vlastnost Context třídy StreamingContext, která je parametrem metody OnSerialized.

Přizpůsobení serializace – příklad

class Pocitadlo { private int pocet; public int Pocet { get { return pocet; } set { pocet = value; } } }

1

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni2 Program serializuje pole tříd Bod. Třída Bod má metodu OnSerialized, která je volána po serializaci bodu. Tato metoda vypíše na obrazovku pořadové číslo bodu, který byl serializován a jeho údaje. Pořadové číslo bodu je uchováno ve vlastnosti Pocet třídy Pocitadlo, jejíž instance poskytuje vlastnost Context třídy StreamingContext, která je parametrem metody OnSerialized.

Přizpůsobení serializace – příklad

[Serializable] class Bod { int x; int y; public Bod(int x, int y) { this.x = x; this.y = y; } [OnSerialized] private void OnSerialized(StreamingContext context) { int pocet = ++((Pocitadlo)context.Context).Pocet; Console.WriteLine("{0}. bod ({1}, {2}) byl serializován", pocet, x, y); } }

2

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni2 Program serializuje pole tříd Bod. Třída Bod má metodu OnSerialized, která je volána po serializaci bodu. Tato metoda vypíše na obrazovku pořadové číslo bodu, který byl serializován a jeho údaje. Pořadové číslo bodu je uchováno ve vlastnosti Pocet třídy Pocitadlo, jejíž instance poskytuje vlastnost Context třídy StreamingContext, která je parametrem metody OnSerialized.

Přizpůsobení serializace – příklad

class Program { static void Main(string[] args) { const int n = 3; Bod[] body = new Bod[n]; for (int i = 0; i < n; i++) { body[i] = new Bod(i, i * 10); } BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File, new Pocitadlo())); using (FileStream fs = new FileStream("data.bin", FileMode.Create)) { bf.Serialize(fs, body); } Console.ReadKey(); } }

3

Předvádějící
Poznámky prezentace
Pr_serializace_prizpusobeni2 Program serializuje pole tříd Bod. Třída Bod má metodu OnSerialized, která je volána po serializaci bodu. Tato metoda vypíše na obrazovku pořadové číslo bodu, který byl serializován a jeho údaje. Pořadové číslo bodu je uchováno ve vlastnosti Pocet třídy Pocitadlo, jejíž instance poskytuje vlastnost Context třídy StreamingContext, která je parametrem metody OnSerialized.

Serializace ve formátu XML ◦ Pro serializaci objektů ve formátu XML slouží třída System.Xml.Serialization.XmlSerializer. ◦ Serializují se nejen datové složky, ale i vlastnosti

třídy (struktury), které obsahují obě přístupové metody (get i set).

Serializace ve formátu XML ◦ Ukládané objekty musí splňovat následující požadavky: Třída (struktura), jejíž instance se má serializovat, musí

obsahovat konstruktor bez parametrů a musí být veřejná. Konstruktor bez parametrů může být deklarován s libovolným modifikátorem přístupových práv (může být i soukromý).

Datové složky a vlastnosti, které se mají serializovat musí být deklarovány jako veřejné a musí být typu, který vyhovuje podmínkám uvedeným v první odrážce. To se týká i datových složek a vlastností objektu, na který se datová složka této třídy přímo nebo nepřímo odkazuje.

Datové složky a vlastnosti, které se nemají serializovat, musí být buď neveřejné nebo se musí deklarovat s atributem XmlIgnore.

Serializace ve formátu XML ◦ Instance třídy XmlSerializer se zpravidla vytváří

pomocí konstruktoru XmlSerializer(Type type) ◦ Parametr type reprezentuje informace o objektu,

který se má serializovat, získané např. operátorem typeof. Konstruktor kontroluje, zda typ, který se má serializovat, splňuje podmínky XML serializace. Pokud zjistí chybu, vyvolá výjimku typu System.InvalidOperationException.

Serializace ve formátu XML ◦ Serializaci lze provést následujícími metodami: void Serialize(Stream stream, object o) zapíše objekt o do datového proudu stream.

object Deserialize(Stream stream) vrací objekt, který načte z datového proudu stream.

◦ Pokud během serializace vznikne chyba, uvedené metody vyvolají výjimku typu System.InvalidOperationException, obsahující případně vnitřní výjimku, popisující skutečnou chybu.

Serializace ve formátu XML ◦ Do jednoho XML souboru (datového proudu) lze

sice za sebou uložit více objektů samostatným voláním metod Serialize, ale při pozdějším čtení prvního objektu metodou Deserialize vznikne výjimka, protože soubor obsahuje dvakrát hlavičku XML. ◦ Při deserializaci se objekt nejprve vytvoří voláním

konstruktoru bez parametrů a potom se načtou jeho složky. Pokud některá složka, která se má serializovat, se nenačte, žádná výjimka nevznikne.

Serializace ve formátu XML ◦ Všechny složky třídy jsou do XML souboru

implicitně ukládány jako prvky XML, jejichž název odpovídá názvu složky třídy. Tento stav lze změnit pomocí dvou atributů: XmlElement – serializuje složku třídy jako prvek XML.

Jedna z verzí tohoto atributu obsahuje název, pod kterým se má prvek serializovat.

XmlAttribute – serializuje složku třídy jako atribut XML. Jedna z verzí tohoto atributu obsahuje název, pod kterým se má prvek serializovat.

Serializace ve formátu XML - příklad ◦ V příkladu se provádí serializace třídy Osoby, která

obsahuje pole tříd Osoba. Třída Osoba obsahuje složky: datové složky rodneCislo, jmeno a interniCislo, vlastnosti RodneCislo a JeMuz. ◦ Z uvedených složek se nebudou serializovat složky rodneCislo, interniCislo a JeMuz. ◦ Vlastnost RodneCislo a jí odpovídající datová

složka jsou typu struktura RodneCislo, jež obsahuje dvě datové složky castA a castB, které se budou serializovat.

Serializace ve formátu XML - příklad

public struct RodneCislo { public int castA; public int castB; public RodneCislo(int castA, int castB) { this.castA = castA; this.castB = castB; } public override string ToString() { return castA + "/" + castB; } }

1

Předvádějící
Poznámky prezentace
Pr_serializace_xml V příkladu se provádí serializace třídy Osoby, která obsahuje pole tříd Osoba. Třída Osoba obsahuje složky: datové složky rodneCislo, jmeno a interniCislo, vlastnosti RodneCislo a JeMuz. Z uvedených složek se nebudou serializovat složky rodneCislo, interniCislo a JeMuz. Vlastnost RodneCislo a jí odpovídající datová složka jsou typu struktura RodneCislo, jež obsahuje dvě datové složky castA a castB, které se budou serializovat.

Serializace ve formátu XML - příklad public class Osoba { private RodneCislo rodneCislo; [XmlAttribute] public string jmeno; [XmlIgnore] public int interniCislo = -1; [XmlElement("BirthNumber")] public RodneCislo RodneCislo { get { return rodneCislo; } set { rodneCislo = value; } } public bool JeMuz { get { return (rodneCislo.castA / 1000) % 10 <= 1; } } public Osoba() { } public Osoba(string jmeno, RodneCislo rodneCislo) { this.jmeno = jmeno; this.rodneCislo = rodneCislo; } public override string ToString() { return jmeno + ", rodné číslo: " + rodneCislo.ToString() + ", interní číslo: " + interniCislo; } }

2

Předvádějící
Poznámky prezentace
Pr_serializace_xml V příkladu se provádí serializace třídy Osoby, která obsahuje pole tříd Osoba. Třída Osoba obsahuje složky: datové složky rodneCislo, jmeno a interniCislo, vlastnosti RodneCislo a JeMuz. Z uvedených složek se nebudou serializovat složky rodneCislo, interniCislo a JeMuz. Vlastnost RodneCislo a jí odpovídající datová složka jsou typu struktura RodneCislo, jež obsahuje dvě datové složky castA a castB, které se budou serializovat.

Serializace ve formátu XML - příklad

public class Osoby { public Osoba[] pole; public Osoby() { } public Osoby(Osoba[] pole) { this.pole = pole; } public void Vypis() { foreach (Osoba item in pole) { Console.WriteLine(item.ToString()); } } }

3

Předvádějící
Poznámky prezentace
Pr_serializace_xml V příkladu se provádí serializace třídy Osoby, která obsahuje pole tříd Osoba. Třída Osoba obsahuje složky: datové složky rodneCislo, jmeno a interniCislo, vlastnosti RodneCislo a JeMuz. Z uvedených složek se nebudou serializovat složky rodneCislo, interniCislo a JeMuz. Vlastnost RodneCislo a jí odpovídající datová složka jsou typu struktura RodneCislo, jež obsahuje dvě datové složky castA a castB, které se budou serializovat.

Serializace ve formátu XML - příklad class Program { static void Main(string[] args) { Osoby osoby = new Osoby(new Osoba[] { new Osoba("Karel", new RodneCislo(661222, 1111)), new Osoba("Jana", new RodneCislo(665222, 2222)) }); XmlSerializer xs = new XmlSerializer(typeof(Osoby)); using (FileStream fs = new FileStream("data.xml", FileMode.Create)) { xs.Serialize(fs, osoby); } osoby.Vypis(); using (FileStream fs = new FileStream("data.xml", FileMode.Open)) { osoby = (Osoby)xs.Deserialize(fs); } Console.WriteLine("Po načtení objektu ze souboru"); osoby.Vypis(); Console.ReadKey(); } }

4

Předvádějící
Poznámky prezentace
Pr_serializace_xml V příkladu se provádí serializace třídy Osoby, která obsahuje pole tříd Osoba. Třída Osoba obsahuje složky: datové složky rodneCislo, jmeno a interniCislo, vlastnosti RodneCislo a JeMuz. Z uvedených složek se nebudou serializovat složky rodneCislo, interniCislo a JeMuz. Vlastnost RodneCislo a jí odpovídající datová složka jsou typu struktura RodneCislo, jež obsahuje dvě datové složky castA a castB, které se budou serializovat.

Serializace ve formátu XML – příklad ◦ Příklad vychází z předchozího příkladu. Liší se v

tom, že místo třídy Osoby se používá pole object[], které obsahuje prvky typu Osoba.

Serializace ve formátu XML – příklad class Program { static void Vypis(object[] osoby) { foreach (Osoba item in osoby) { Console.WriteLine(item.ToString()); } } static void Main(string[] args) { object[] osoby = new object[] { new Osoba("Karel", new RodneCislo(661222, 1111)), new Osoba("Jana", new RodneCislo(665222, 2222)) }; XmlSerializer xs = new XmlSerializer(typeof(object[]), new Type[] { typeof(Osoba) }); using (FileStream fs = new FileStream("data.xml", FileMode.Create)) { xs.Serialize(fs, osoby); } Vypis(osoby); osoby = null; using (FileStream fs = new FileStream("data.xml", FileMode.Open)) { osoby = (object[])xs.Deserialize(fs); } Console.WriteLine("Po načtení objektu ze souboru"); Vypis(osoby); Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_serializace_xml2 Příklad vychází z předchozího příkladu. Liší se v tom, že místo třídy Osoby se používá pole object[], které obsahuje prvky typu Osoba.

Serializace ve formátu XML ◦ Pokud se má serializovat kolekce prvků typu object (např. object[], ArrayList), musí se použít konstruktor XmlSerializer(Type type, Type[] extraTypes) ◦ Parametr extraTypes reprezentuje pole informací o

objektech, které mohou obsahovat prvky kolekce.s

Využívá se statická metoda Format třídy string, případně její přetížené varianty.

◦ string Format(string format, params object[] args)

Tvar odpovídá jedné z variant metod Write a WriteLine používaných ve výstupech do konzoly nebo jiného datového proudu. ◦ Tento tvar umají i jiné metody, např. metoda AppendFormat třídy StringBuilder.

◦ string Format(string format, params object[] args)

Parametr format představuje složený

formátovací řetězec (angl. composite format string), v němž se vyskytují složené závorky s pořadovými čísly parametrů pole args. Tyto složené závorky se nazývají tzv. formátovací položky (angl. format items). První parametr pole args má pořadové číslo 0.

Metoda vrací řetězec, který vznikne z řetězce format nahrazením formátovacích položek textovou podobou formátovaných parametrů.

Příklad

class Program { static void Main(string[] args) { int a = 10; double b = 2.5; string s = string.Format("a = {0}, b = {1}", a, b); Console.WriteLine(s); s = string.Format("a = {{{0}}}, b = {{{1}}}", a, b); Console.WriteLine(s); Console.ReadKey(); } } }

Předvádějící
Poznámky prezentace
V uvedeném příkladu má složený formátovací řetězec formátovací položky {0} a {1}. Výstup programu bude následující: a = 10, b = 2,5 Pokud se má ve výsledném textu objevit levá nebo pravá složená závorka, musí se ve složeném formátovacím řetězci uvést dvě složené závorky za sebou. Např. následující příkaz string s = string.Format("a = {{{0}}}, b = {{{1}}}", a, b); uloží do řetězce s text a = {10}, b = {2,5}

vyskytující se ve složeném formátovacím řetězci má následující syntaxi: ◦ {index část_zarovnánínep část_formátovací_řetězecnep } část_zarovnání se píše za , část_formátovací_řetězec se píše za : Mezi levou složenou závorkou a částí index nesmí být mezera. Index je povinná část, která reprezentuje pořadové číslo

formátovaného parametru, počínaje nulou. Více formátovacích položek se může odkazovat na stejný formátovaný parametr. Formátovací řetězec se může odkazovat na pořadová čísla v libovolném pořadí, např. "{b = {1}, a = {0}".

Část zarovnání je nepovinná a představuje minimální šířku vyhrazeného prostoru zadanou jako celé číslo se znaménkem. Je-li uvedena, musí být od části index oddělena čárkou. Hodnota zarovnání se ignoruje, pokud skutečná délka výsledného textu formátovací položky je větší než absolutní hodnota zarovnání. Způsob zarovnání je dán znaménkem hodnoty zarovnání: kladná hodnota – výsledný text je zarovnán napravo vyhrazeného

prostoru, záporná hodnota – výsledný text je zarovnán nalevo vyhrazeného

prostoru.

Zbývající prostor vyhrazeného prostoru je vyplněn mezerami.

Část formátovací řetězec je nepovinná a představuje řetězec určující formát výsledného textu pro daný formátovaný parametr. Možnosti závisí na typu formátovaného parametru (číslo, datum a čas, výčtový typ apod.). Není-li formátovací řetězec uveden, použije se všeobecný formát "G".

Předávání formátovaného parametru 1. Je-li hodnota formátovaného parametru null, výsledkem je prázdný řetězec (""). 2. Jinak, jestliže typ formátovaného parametru implementuje rozhraní ICustomFormatter, je volána metoda Format tohoto rozhraní, mající tvar string Format(string format, object arg, IFormatProvider fp) kde: format reprezentuje formátovací řetězec, arg formátovaný parametr a fp

formátovací informace závislé na kultuře. 3. Jinak, jestliže typ formátovaného parametru implementuje rozhraní IFormattable, je volána metoda ToString tohoto rozhraní, mající tvar string ToString(string format, IFormatProvider fp) kde parametry mají stejný význam jako ve 2. variantě.

4. Jinak je pro formátovaný parametr volána metoda ToString třídy object (může být předefinována), mající tvar string ToString()

Předávání formátovaného parametru ◦ Způsob zarovnání je aplikován až pro hodnotu, která je

výsledkem jednoho z uvedených kroků. ◦ Pro základní číselné datové typy (int, float, decimal apod.),

výčtové typy a typ datum a čas se použije 3. varianta uvedeného postupu, protože tyto typy implementují rozhraní IFormattable.

◦ Typy char a string neimplementují žádné z uvedených rozhraní, a proto se pro ně použije 4. varianta.

◦ Pokud index má hodnotu neexistujícího pořadového čísla formátovaného parametru, nebo pokud je uveden jinak nesprávný zápis formátovací položky, vyvolá se výjimka typu System.FormatException.

Formátovací řetězec má tvar Axx ◦ A – jeden znak, formátovací specifikátor ◦ xx – přesnost, celé číslo 0-99

Standardní formát vychází z formátu čísel třídy System.Globalization.NumberFormatInfo pro aktuální kulturu, implicitně podle nastavení Windows.

Přehled formátovacích specifikátorů Formátovací

specifikátor Význam

C nebo c Formát měny specifikovaný v třídě NumberFormatInfo. Specifikátor přesnosti udává počet desetinných míst. Je-li vynechán, použije se přesnost, kterou poskytuje vlastnost CurrencyDecimalDigits.

D nebo d Číslo v desítkové soustavě. Lze použít pouze pro celočíselného typy. Specifikátor přesnosti udává minimální počet číslic. Je-li počet číslic formátovaného parametru menší než specifikátor přesnosti, číslo je doplněno zleva nulami.

E nebo e Semilogaritmický tvar. Specifikátor přesnosti udává počet desetinných míst. Je-li vynechán, číslo se zobrazí s přesností na 6 desetinných míst. Velikost písmene exponentu odpovídá velikosti formátovacího specifikátoru.

F nebo f Tvar s pevnou řádovou čárkou. Specifikátor přesnosti udává počet desetinných míst. Je-li vynechán, použije se přesnost, kterou poskytuje vlastnost NumberDecimalDigits.

G nebo g Všeobecný formát. Je-li specifikátor přesnosti vynechán nebo má hodnotu nula, použije se implicitní přesnost pro daný datový typ (viz nápověda).

Přehled formátovacích specifikátorů

Formátovací specifikátor

Význam

N nebo n

Číslo s oddělovačem tisíců specifikovaným hodnotou vlastnosti NumberGroupSeparator. Počet číslic ve skupině udává vlastnost NumberGroupSizes (implicitně 3). Specifikátor přesnosti udává počet desetinných míst. Je-li vynechán, použije se přesnost, kterou poskytuje vlastnost NumberDecimalDigits.

P nebo p

Číslo se symbolem procent podle vzoru, který poskytují vlastnosti PercentNegativePattern a PercentPositivePattern pro kladné a záporné číslo. Vstupní hodnota je vynásobena 100. Specifikátor přesnosti udává počet desetinných míst. Je-li vynechán, použije se přesnost, kterou udává vlastnost PercentDecimalDigits. Výsledný formát ovlivňují další vlastnosti: PercentGroupSeparator, PercentGroupSizes, PercentSymbol.

R nebo r

Tvar „cesta tam a zpět“ (angl. „round-trip“). Lze použít pouze pro typy double a float. Tento tvar garantuje, že reálné číslo zkonvertované na řetězec bude možné zkonvertovat zpět na stejnou číselnou hodnotu. Nejprve se testuje převod čísla na text s použitím implicitní přesnosti (15 desetinných míst pro typ double, 7 míst pro typ float). Jestliže takto zkonvertované číslo lze převést zpět na stejnou číselnou hodnotu, použije se pro číslo všeobecný formát G. Jinak se číslo převede na text s přesností 17 desetinných míst pro typ double a 9 míst pro typ float. Případný specifikátor přesnosti se ignoruje.

X nebo x

Hexadecimální tvar. Lze použít pouze pro celočíselné typy. Velikost písmen A až F odpovídá velikosti formátovacího specifikátoru. Specifikátor přesnosti udává minimální počet číslic. Je-li počet číslic formátovaného parametru menší než specifikátor přesnosti, číslo je doplněno zleva nulami.

Oddělovač desetinných míst poskytují vlastnosti CurrencyDecimalSeparator, NumberDecimalSeparator, PercentDecimalSeparator třídy NumberFormatInfo podle použitého formátovacího specifikátoru.

Jestliže hodnota formátovaného parametru typu double nebo float je kladné nebo záporné nekonečno nebo NaN, bez ohledu na použitý formátovací specifikátor je výsledkem text, který poskytují vlastnosti PositiveInfinitySymbol, NegativeInfinitySymbol a NaNSymbol třídy NumberFormatInfo.

Příklady pro českou kulturu

Formátovací položka

Hodnota Výstup

{0:C} 1234 1 234,00 Kč

{0:C1} 123.456 1 234,5 Kč

{0:D} 1234 1234

{0:D6} 1234 001234

{0:E} 1234 1,234000E+003

{0:e2} 123.456 1,23e+002

{0:F} 1234 1234,00

{0:F1} 123.456 123,5

{0:G} 123.456 123,456

Příklady pro českou kulturu

Formátovací položka

Hodnota Výstup

{0:G1} 123.456 1E+02

{0:G2} 123.456 1,2E+02

{0:N} 1234 1 234,00

{0:P} 1234 123 400,00%

{0:P0} 123.456 12 346%

{0:R} 123.456 123,456

{0:R1} 123.456 123,456

Uživatelem definovaný formátovací řetězec ◦ Může být složen z následujících specifikátorů

Formátovací specifikátor

Význam

0 Jestliže hodnota formátovaného parametru má číslici (včetně nevýznamné nuly) na pozici, na které je ve formátovacím řetězci uvedena tato nula, potom se tato číslice zobrazí ve výsledném textu. Pozice nuly nejvíce vlevo a nuly nejvíce vpravo ve formátovacím řetězci určují rozsah číslic, které se vždy zobrazí ve výsledném textu (včetně nevýznamných nul).

# Jestliže hodnota formátovaného parametru má číslici jinou než nevýznamnou nulu na pozici, na které je ve formátovacím řetězci uveden symbol #, potom se tato číslice zobrazí ve výsledném textu.

Uživatelem definovaný formátovací řetězec ◦ Může být složen z následujících specifikátorů

Formátovací specifikátor

Význam

. Pozice desetinné čárky. , Je-li tento specifikátor umístěn jinde než

bezprostředně před specifikátorem desetinné čárky, představuje oddělovač tisíců. Příslušné vlastnosti třídy NumberFormatInfo určují symbol oddělovače a počet číslic ve skupině. Počet číslic ve skupině nezávisí na počtu specifikátorů 0 nebo # uvedených před nebo za specifikátorem oddělovače tisíců. Je-li tento specifikátor uveden bezprostředně před specifikátorem desetinné čárky resp. před implicitně zobrazenou desetinnou čárkou, hodnota formátovaného parametru je podělena 1000 pro každý výskyt tohoto specifikátoru.

Uživatelem definovaný formátovací řetězec ◦ Může být složen z následujících specifikátorů

Formátovací specifikátor

Význam

% Pro každý výskyt tohoto specifikátoru se nejprve hodnota formátovaného parametru vynásobí 100 a na pozici, na které je ve formátovacím řetězci uveden tento specifikátor, se ve výsledném textu zobrazí znak procent daného vlastností PercentNegativePattern resp. PercentPositivePattern .

E0 nebo e0 E+0 nebo e+0 E-0 nebo e-0

Je-li ve formátovacím řetězci uveden některý z těchto specifikátorů, hodnota se zobrazí v semilogaritmickém tvaru. Počet nul za symbolem E resp. e specifikuje počet číslic, které se zobrazí v exponentu. Je-li ve specifikátoru uveden znak +, zobrazí se znaménko + u kladné hodnoty exponentu, jinak se u kladné hodnoty exponentu znaménko nezobrazí. U záporné hodnoty exponentu se zobrazí znaménko – vždy bez ohledu na použitý typ tohoto specifikátoru.

Uživatelem definovaný formátovací řetězec ◦ Může být složen z následujících specifikátorů

Formátovací specifikátor

Význam

\ Znak řídící posloupnosti (angl. escape sequence). Např. \t způsobí vložení znaku tabulátoru do výsledného textu.

'ABC' nebo "ABC"

Znaky v apostrofech nebo uvozovkách se zobrazí ve výsledném textu ve stejném tvaru jako ve formátovacím řetězci.

Uživatelem definovaný formátovací řetězec ◦ Může být složen z následujících specifikátorů

Formátovací specifikátor

Význam

; Oddělovač oddílů pro kladná, záporná a nulová čísla. Formátovací řetězec může obsahovat: • jeden oddíl – použije se pro všechny hodnoty. • dva oddíly – první oddíl se použije pro kladné a nulové

hodnoty, druhý oddíl pro záporné hodnoty. • tři oddíly – první oddíl se použije pro kladné, druhý pro

záporné a třetí pro nulové hodnoty. jiný zna k Znaky v apostrofech nebo uvozovkách se zobrazí ve

výsledném textu ve stejném tvaru jako ve formátovacím řetězci.

Uživatelem definovaný formátovací řetězec ◦ Příklady pro českou kulturu

Formátovací položka

Hodnota Výstup

{0:00000.00} 1234 01234,00

{0:00000.00} 123.456 00123,46

{0:##.##} 1234 1234

{0:##.##} 123.456 123,46

{0:#,#} 1234 1 234

{0:#,0.00} 12345.678 12 345,68

{0:0,.0} 123456.78 123,5

{0:0,,.0} 123456.78 0,1

{0:0.0%} 12345 123400,0%

{0:0.0%} 123.456 12345,6%

Uživatelem definovaný formátovací řetězec ◦ Příklady pro českou kulturu

Formátovací položka

Hodnota Výstup

{0:0.0E+00} 123.456 1,2E+02

{0:0.0E-00} 123.456 1,2E02

{0:00\t00} 1234 12 34

{0:0.0' km'} 123.456 123,5 km

{0:0;(0);'nula'} 12 12

{0:0;(0);'nula'} -12 (12)

{0:0;(0);'nula'} 0 nula

Datum a čas ◦ Pro práci s datem a časem slouží struktura DateTime, která

umožňuje uchovat čas od půlnoci 1. ledna roku 1 do 31. prosince roku 9999, 23:59:59.

◦ Časové hodnoty jsou měřeny ve stovkách nanosekund, tzv. „tikách“ (angl. ticks). Konkrétní datum je interně reprezentován jako počet tiků od 1.1.0001 00:00:00 v gregoriánském kalendáři.

Datum a čas ◦ Od verze .NET 2.0 struktura DateTime obsahuje

64bitovou celočíselnou datovou složku (typu ulong) složenou ze 62bitové hodnoty počtu tiků a 2bitové hodnoty vlastnosti Kind výčtového typu DateTimeKind, který má následující konstanty: Local – čas reprezentuje lokální čas počítače. Utc – čas reprezentuje koordinovaný světový čas

(angl. Coordinated Universal Time). Unspecified – druh času není specifikován. ◦ Vlastnost Kind je používána k převodu mezi UTC

a lokálním časem. Rozdíl mezi GMT a UTC?

Předvádějící
Poznámky prezentace
Před verzí .NET 2.0 neobsahovala struktura DateTime 2-bitovou hodnotu Kind. Čas UTC je také někdy nazýván „Zulu time“ a je označován písmenem Z za časovým údajem. Jednotlivá časová pásma jsou definována svými odchylkami od UTC. UTC je nástupcem greenwichského středního času GMT (angl. Greenwich Mean Time). GMT udává čas platný v časovém pásmu vůči nultému (základnímu) poledníku a je založen na rotaci Země. Zatímco UTC je založen na atomových hodinách. Protože se rotace Země mírně zpomaluje, GMT se oproti UTC postupně zpožďuje. Aby se UTC dal používat v praktickém životě, který je s rotací Země spjatý, je udržován v rozmezí ±0,9 sekund od přesného atomového času. Pokud je tato odchylka překročena, je přidána přestupná sekunda. K tomu dochází průměrně jednou za rok až rok a půl. UTC čas je vhodný pro výpočty, porovnání a ukládání data a času do souboru. Lokální čas je vhodný pro zobrazení uživateli. UTC čas se nemění při přechodu na letní nebo zimní čas, zatímco lokální čas ano.

Datum a čas ◦ Struktura DateTime nabízí celou řadu konstruktorů s

následujícími parametry: počet tiků se zadaným nebo nespecifikovaným druhem

času, rok, měsíc, den v gregoriánském kalendáři nebo v

zadaném kalendáři (např. čínský, juliánský), rok, měsíc, den, hodina, minuta, sekunda v

gregoriánském kalendáři nebo v zadaném kalendáři se zadaným nebo nespecifikovaným druhem času,

rok, měsíc, den, hodin, minuta, sekunda, milisekunda v gregoriánském kalendáři nebo v zadaném kalendáři se zadaným nebo nespecifikovaným druhem času.

Datum a čas ◦ Vlastnosti poskytují jednotlivé části data a času (rok,

měsíc, den v měsíci, den v týdnu, den v roce, hodina, minuta, sekunda, milisekunda, počet tiků), druh času a dále vlastnosti: Date – poskytuje datum s nulovým časem, TimeOfDay – poskytuje čas v rámci dne typu TimeSpan, Today – poskytuje aktuální datum (s nulovým časem) v

počítači v lokálním čase, Now – poskytuje aktuální datum a čas počítače v lokálním

čase, UtcNow – poskytuje aktuální datum a čas počítače v UTC

čase.

Datum a čas ◦ Významné metody třídy DateTime. DateTime Add(TimeSpan value) Přičte hodnotu časového intervalu k hodnotě this.

DateTime AddJednotky(value) Skupina metod, které přičtou k hodnotě this zápornou nebo

kladnou hodnotu zadaných jednotek (roky až milisekundy nebo tiky).

metody CompareTo, Compare a Equals relační operátory Metody a operátory porovnávají dva časy podle počtu tiků

bez ohledu na druh času.

Datum a čas ◦ Významné metody třídy DateTime. static bool IsLeapYear(int year) Vrací true, pokud zadaný rok je přestupný.

bool IsDaylightSavingTime() Vrací true, pokud this datum a čas patří do období letního

času. static DateTime operator + (DateTime d, TimeSpan t) static DateTime operator - (DateTime d, TimeSpan t) DateTime Subtract(TimeSpan value) Přičte nebo odečte hodnotu časového intervalu k resp. od

hodnoty this.

Datum a čas ◦ Významné metody třídy DateTime. static TimeSpan operator - (DateTime d1, DateTime d2) TimeSpan Subtract(DateTime value)

Odečte od sebe dvě data. DateTime ToLocalTime() Převádí this hodnotu na lokální čas. Je-li this hodnota v

nespecifikovaném čase, chápe se jako v čase UTC. DateTime ToUniversalTime() Převádí this hodnotu na UTC čas. Je-li this hodnota v

nespecifikovaném čase, chápe se jako v lokálním čase.

Datum a čas ◦ Významné metody třídy DateTime. string ToLongTimeString() string ToShortTimeString() Převádí datum a čas na řetězec znaků reprezentující

dlouhý resp. krátký formát času aktuální kultury. Odpovídá formátovacímu specifikátoru T resp. t (viz dále).

string ToString() Převádí datum a čas na řetězec znaků reprezentující

krátký formát data a dlouhý formát času. Odpovídá formátovacímu specifikátoru G (viz dále).

Datum a čas - příklad class Program { static void Main(string[] args) { DateTime d = new DateTime(); Console.WriteLine("Nulová hodnota reprezentuje čas {0}", d); DateTime d2 = new DateTime(2008, 1, 31, 12, 0, 0, DateTimeKind.Utc); Console.WriteLine("Čas v UTC: {0} ", d2); DateTime d3 = d2.ToLocalTime(); Console.WriteLine("Čas po převodu na lokální čas: {0}", d3); d2 = DateTime.UtcNow; Console.WriteLine("Aktuální UTC čas: {0} ", d2); d3 = DateTime.Now; Console.WriteLine("Aktuální lokální čas: {0} ", d3); TimeSpan ts = d3 - d2; Console.WriteLine("Počet hodin rozdílu mezi lokálním a UTC časem:" + " {0}", ts.Hours); Console.WriteLine("Aktuální UTC čas po přičtení 1,5 dne: {0}", d2.AddDays(1.5)); Console.WriteLine("Aktuální UTC čas po přičtení 1 dne a 2 h " + "a 3 s: {0}", d2.Add(new TimeSpan(1, 2, 3, 0))); Console.ReadKey(); } }

Předvádějící
Poznámky prezentace
Pr_ datetime Výstup programu bude následující: Nulová hodnota reprezentuje čas 1.1.0001 0:00:00 Čas v UTC: 31.1.2008 12:00:00 Čas po převodu na lokální čas: 31.1.2008 13:00:00 Aktuální UTC čas: 11.3.2008 12:31:10 Aktuální lokální čas: 11.3.2008 13:31:10 Počet hodin rozdílu mezi lokálním a UTC časem: 1 Aktuální UTC čas po přičtení 1,5 dne: 13.3.2008 0:31:10 Aktuální UTC čas po přičtení 1 dne a 2 h a 3 s: 12.3.2008 14:34:10 Stránka 155 z 161

Standardní ◦ Vychází z třídy System.Globalization.DateTimeFormatInfo pro aktuální kulturu, implicitně podle Windows. ◦ Přehled specifikátorů Formátovací

specifikátor Význam

d Zkrácený formát data podle vzoru vlastnosti ShortDatePattern.

D Dlouhý formát data podle vzoru vlastnosti LongDatePattern.

f Kombinace specifikátoru D a t oddělená mezerou.

F Dlouhý formát data a času podle vzoru vlastnosti FullDateTimePattern.

Standardní ◦ Přehled specifikátorů Formátovací

specifikátor Význam

g Kombinace specifikátoru d a t oddělená mezerou. G Kombinace specifikátoru d a T oddělená mezerou.

M nebo m Formát data podle vzoru vlastnosti MonthDayPattern. o Tvar „cesta tam a zpět“ (angl. „round-trip“). Tento tvar garantuje, že datum a čas

zkonvertovaný na řetězec bude možné zkonvertovat zpět na stejnou hodnotu. Má stejný tvar bez ohledu na aktuální kulturu.

R nebo r Formát data a času podle vzoru vlastnosti RFC1123Pattern daného specifikací RFC (Request for Comments) 1123. Uvedená vlastnost je pouze pro čtení, tedy má stejný tvar bez ohledu na aktuální kulturu.

s Formát data a času podle vzoru vlastnosti SortableDateTimePattern daného normou ISO 8601 umožňující třídění. Uvedená vlastnost je pouze pro čtení, tedy má stejný tvar bez ohledu na aktuální kulturu.

Standardní ◦ Přehled specifikátorů Formátovací

specifikátor Význam

t Zkrácený formát času podle vzoru vlastnosti ShortTimePattern.

T Dlouhý formát času podle vzoru vlastnosti LongTimePattern.

u Univerzální formát data a času umožňující třídění podle vzoru vlastnosti UniversalSortableDateTimePattern. Uvedená vlastnost je pouze pro čtení, tedy má stejný tvar bez ohledu na aktuální kulturu. Není prováděna konverze na jiné časové pásmo.

U Univerzální formát data a času umožňující třídění. Odpovídá specifikátoru F s tím rozdílem, že čas je převeden na pásmo UTC (Coordinated Universal Time).

Y nebo y Formát data podle vzoru vlastnosti YearMonthPattern.

Standardní ◦ Příklad pro českou kulturu a instanci new DateTime(2007, 1, 20, 23, 5, 10, 458)

Formátovací specifikátor

Výstup

d 20.1.2007 D 20. ledna 2007

f 20. ledna 2007 23:05 F 20. ledna 2007 23:05:10 g 20.1.2007 23:05

G 20.1.2007 23:05:10

M 20 ledna

o 2007-01-20T23:05:10.4580000

Standardní ◦ Příklad pro českou kulturu a instanci new DateTime(2007, 1, 20, 23, 5, 10, 458)

Formátovací specifikátor

Výstup

R Sat, 20 Jan 2007 23:05:10 GMT s 2007-01-20T23:05:10

t 23:05 T 23:05:10 u 2007-01-20 23:05:10Z

U 20. ledna 2007 22:05:10

Y leden 2007

Uživatelem definovaný formát ◦ Přehled specifikátorů Formátovací

specifikátor Význam

d Den v měsíci v rozsahu 1 až 31 bez nuly na začátku. dd Den v měsíci v rozsahu 01 až 31 s nulou na začátku.

ddd Zkrácený název dne v týdnu daný vlastností AbbreviatedDayNames.

dddd Úplný název dne v týdnu daný vlastností DayNames. f, ff, …, fffffff

Desetiny (f), setiny (ff), tisíciny (fff) atd. sekund se zobrazením koncových nevýznamných nul.

F, FF, …, FFFFFFF

Dtto jako f ale bez zobrazení koncových nevýznamných nul.

Uživatelem definovaný formát ◦ Přehled specifikátorů Formátovací

specifikátor Význam

g Zkratka éry. Pro český kalendář (gregoriánský) se jedná o zkratku „n. l.“ (našeho letopočtu).

h Hodiny v rozsahu 1 až 12 bez nuly na začátku.

hh Hodiny v rozsahu 01 až 12 s nulou na začátku. H Hodiny v rozsahu 0 až 23 bez nuly na začátku. HH Hodiny v rozsahu 0 až 23 s nulou na začátku. K Text, který informuje o druhu časového pásma. Pro pásmo UTC

se zobrazí Z, pro pásmo „local“ se zobrazí čas ve tvaru +hh:mm resp. –hh:mm s uvedením o kolik hodin je rozdíl mezi místním časovým pásmem a pásmem GMT. Pro nespecifikované pásmo se nezobrazí nic.

Uživatelem definovaný formát ◦ Přehled specifikátorů Formátovací

specifikátor Význam

m Minuty v rozsahu 0 až 59 bez nuly na začátku. mm Minuty v rozsahu 00 až 59 s nulou na začátku.

M Měsíc v rozsahu 1 až 12 bez nuly na začátku. MM Měsíc v rozsahu 01 až 12 bez nuly na začátku. MMM Zkrácený název měsíce daný vlastností

AbbreviatedMonthNames. MMMM Úplný název měsíce daný vlastností MonthNames.

Uživatelem definovaný formát ◦ Přehled specifikátorů Formátovací

specifikátor Význam

s Sekundy v rozsahu 0 až 59 bez nuly na začátku. ss Sekundy v rozsahu 00 až 59 s nulou na začátku.

t Reprezentuje první znak zkratky A.M./P.M. (v aktuální kultuře) daný vlastností AMDesignator a PMDesignator.

tt Reprezentuje zkratku A.M/P.M. (v aktuální kultuře) daný vlastností AMDesignator a PMDesignator.

y Dvouciferný rok bez nuly na začátku. yy Dvouciferný rok s nulou na začátku. yyy Tříciferný rok s nulami na začátku. Pokud je rok větší než 999,

zobrazí se celý. yyyy… Čtyř a víceciferný rok s nulami na začátku.

Uživatelem definovaný formát ◦ Přehled specifikátorů Formátovací

specifikátor Význam

z Posun časového pásma od GMT v hodinách bez nuly na začátku, např. +1.

zz Posun časového pásma od GMT v hodinách s nulou na začátku, např. +01.

zzz Posun časového pásma od GMT v hodinách a minutách, např. +01:00.

: Oddělovač času daný vlastností TimeSeparator.

/ Oddělovač data daný vlastností DateSeparator.

'ABC' "ABC"

Znaky v apostrofech nebo uvozovkách se zobrazí ve výsledném textu ve stejném tvaru jako ve formátovacím řetězci.

%c Znak c reprezentuje jeden z formátovacích specifikátorů, který je ve formátovacím řetězci uveden samostatně. Např. formátovací řetězec {0:d} zobrazí datum ve zkráceném tvaru, zatímco řetězec {0:%d} zobrazí den v měsíci.

\c Ve výsledném textu se zobrazí znak c.

Uživatelem definovaný formát ◦ Příklad pro českou kulturu Formátovací položka Výstup

{0:d. M. yy} 20. 1. 07 {0:dd/MM/yyy} 20.01.2007 {0:ddd dd.MMM.yyyy} so 20.I.2007 {0:dddd dd.MMMM yyyy} sobota 20.ledna 2007 {0:d.m.yy g} 20.5.07 n. l. {0:hh:mm:ss,ffff} 11:05:10,4580 {0:HH:mm:ss,FFFF} 23:05:10,458 {0:HH:mm:ss t} 23:05:10 o {0:HH:mm:ss tt} 23:05:10 odp. {0:HH:mm:ss z} 23:05:10 +1

Předvádějící
Poznámky prezentace
{0:HH:mm:ss K} 23:05:10 {0:%d} 20

◦ Přehled specifikátorů

Formátovací specifikátor

Význam

G/g Hodnota se převede na řetězec. Není-li to možné, převede se na celé číslo. Je-li výčtový typ deklarován s atributem Flags, pokusí se převést hodnotu na textovou reprezentaci kombinace příznaků oddělených čárkou.

F/f Dtto G nebo g s tím rozdílem, že se pokusí převést hodnotu na textovou reprezentaci kombinace výčtových konstant, i když výčtový typ není deklarován s atributem Flags.

D/d Hodnota se převede na celé číslo v desítkové soustavě. X/x Reprezentuje zkratku A.M/P.M. (v aktuální kultuře) daný

vlastností AMDesignator a PMDesignator.

Příklad ◦ Jsou deklarovány následující výčtové typy: enum Den { Pondělí = 0x01, Úterý = 0x02, Středa = 0x04, Čtvrtek = 0x08, Pátek = 0x10, Sobota = 0x20, Neděle = 0x40 }

[Flags]

enum Dny { Pondělí = 0x01, Úterý = 0x02, Středa = 0x04, Čtvrtek = 0x08, Pátek = 0x10, Sobota = 0x20, Neděle = 0x40 }

Příklad

Formátovací položka

Hodnota Výstup

{0:G} Den.Středa Středa

{0:G} Den.Středa+1 5

{0:G} Dny.Pondělí | Dny.Středa Pondělí, Středa

{0:G} Den.Pondělí | Den.Středa 5

{0:F} Dny.Pondělí | Dny.Středa Pondělí, Středa

{0:F} Den.Pondělí | Den.Středa Pondělí, Středa

{0:D} Den.Pátek 16

{0:X} Den.Pátek 00000010

Kultura je reprezentována třídou System.Globalization.CultureInfo.

Jeden z konstruktorů této třídy má parametr typu string reprezentující název kultury a podkultury. Např. cs-CZ pro českou kulturu – podkulturu Česká republika, de-AT pro německou kulturu – podkulturu Rakousko apod.

Pro každý podproces aplikace lze nastavit samostatnou kulturu.

Aktuální kulturu pro daný podproces poskytuje nebo nastavuje vlastnost CurrentCulture třídy System.Threading.Thread.

Aktuální podproces aplikace poskytuje statická vlastnost CurrentThread třídy Thread.

Aktuální kulturu aktuálního podprocesu lze tedy nastavit nebo získat pomocí výrazu

System.Threading.Thread.CurrentThread.CurrentCulture nebo získat pomocí výrazu System.Globalization.CultureInfo.CurrentCulture.

Příklad CultureInfo ci = new CultureInfo("cs-CZ"); // Czech

ci.NumberFormat.NumberGroupSeparator = "."; // oddělovač tisíců

// v číselném formátu

Thread.CurrentThread.CurrentCulture = ci;

Console.WriteLine("{0:N}", 1234);

Předvádějící
Poznámky prezentace
V příkladu se vytvoří instance české kultury. V ní se změní oddělovač tisíců na tečku. Instance kultury se nastaví jako aktuální kultura aplikace a vypíše se celé číslo pomocí formátovacího řetězce N. Výstup programu bude následující: 1.234,00

Třída string nabízí ještě další metodu Format ◦ string Format(IFormatProvider provider, string format, params object[] args)

◦ Parametr provider reprezentuje poskytovatele formátu.

Rozhraní IFormatProvider implementují třídy NumberFormatInfo, DateTimeFormatInfo a CultureInfo.

V metodě Format bez parametru typu IFormatProvider ◦ string Format(string format, params object[] args)

se použije jako poskytovatel formátu aktuální kultura aktuálního podprocesu.

Příklad DateTime d = new DateTime(2007, 2, 28, 10, 23, 5);

Console.WriteLine(string.Format(new CultureInfo("de-De"), "{0:D}", d));

Console.WriteLine(string.Format(NumberFormatInfo.InvariantInfo,

"{0:0.00}", 123.458));

Výstup programu bude: Mittwoch, 28. Februar 2007

123.46

string ToString (IFormatProvider provider) ◦ převádí hodnotu na řetězec s použitím všeobecného

formátu ("G") a informací o kultuře

string ToString (string format) ◦ převádí hodnotu na řetězec s použitím zadaného

formátovacího řetězce pro aktuální kulturu

string ToString (string format, IFormatProvider provider) ◦ převádí hodnotu na řetězec s použitím zadaného

formátovacího řetězce a informací o kultuře

Příklad static void Main(string[] args) { double a = 123.456; DateTime d = new DateTime(2007, 2, 28); Console.WriteLine(d.ToString(new CultureInfo("en-US"))); Console.WriteLine(a.ToString("0.00")); Console.WriteLine(a.ToString("0.00", new CultureInfo("en-US"))); Console.ReadKey(); } Výstup programu bude: 2/28/2007 12:00:00 AM 123,46 123.46

Think about your users and their needs before you start

building the code, and they’ll be happy with the final

product once you’re done!