KST/ICSHP - 5. a 6. přednáška
-
Upload
jan-hridel -
Category
Education
-
view
170 -
download
0
Transcript of KST/ICSHP - 5. a 6. přednáška
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
Čá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í
Čá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(){} // ... }
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í
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ří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ří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říklad
class Sestava : Ivypisovací { public void Vypis() { Console.WriteLine("Výpis údajů sestavy"); } }
3
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
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ří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
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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ř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ří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ří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ří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(); } }
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ří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ří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
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ří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ří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); } }
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ří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
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ří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
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
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
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 } } }
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]); } }
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ří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
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
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
Č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
Č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
Č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
Č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
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"
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
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
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
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
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
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ří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ří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ř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ř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ř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ř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ř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ř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
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
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
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
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
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(); } }
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(); } } }
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?
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(); } }
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ř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);
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