Fakulta elektrotechniky a informatiky Univerzita Pardubice 2014/2015 poslední přednáška a materiál k samostudiu
@h0nza
Raději už na začátku položte o otázku víc Vás to nic nestojí a ušetříte si spoustu práce. ;)
Vnořené třídy Stejně jako v C++ lze deklarovat uvnitř třídy (struktury) vnořenou třídu (strukturu), která se stane její složkou a vztahují se na ní přístupová práva stejně jako na další složky. Rozdíly oproti C++ V C++ neplatí žádné výjimky při přístupu z vnější třídy ke složkám vnitřní a naopak. V C# mohou složky vnitřní třídy přistupovat ke všem složkám vnější třídy. V C# mohou složky vnější třídy přistupovat k veřejným nebo interním složkám vnitřní třídy.
Vnořené třídy Doporučení Nedeklarovat vnitřní veřejné typy. Vnořené typy by měly být používány jen vnějšími typy. Příklad class A private int x; public class B private int y; public void f() A a = new A(); a.x = 15; // OK public void g() B b = new B(); // b.y = 25; // Chyba Vytvoření instance vnořené třídy B mimo tělo vnější třídy lze provést příkazem A.B b = new A.B();
Dědičnost V C# pouze jednoduchá dědičnost. Pokud není uveden předek třídy, doplní překladač jako předka třídu object. Potomek může předka zastoupit V C# lze odvozovat pouze od třídy (ne od struktury) a dědit může opět jen třída (ne struktura). Syntaxe :jméno Jméno identifikátor bázové třídy (možno i s uvedením jmenného prostoru)
Dědičnost V C# je veškeré dědění veřejné. Ve specifikaci předka nelze uvést přístupová práva ani modifikátor virtual. Potomek dědí všechny složky předka mimo konstruktorů a destruktorů. Některé složky (soukromé a je-li potomek deklarován v jiném sestavení, tak i interní) však nemusí být v potomkovi přístupné.
Dědičnost Zastíněné složky Žádnou zděděnou složku nelze v potomkovi odstranit. Zděděné složky lze zastínit použitím stejného identifikátoru (u metod včetně signatury) a užitím modifikátoru new. Zastínit lze i virtuální metody, vlastnosti a indexery. Možnost přístupu k zastíněným rodičovským složkám zůstává pomocí klíčového slova base.
Dědičnost Příklad 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. Statické složky nemohou být virtuální A musí mít stejná přístupová práva.
Dědičnost Polymorfismus příklad 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 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) Takto zapečetěnou složku nelze v potomkovi předefinovat. 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.
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 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 1 interface IVypisovací void Vypis(); interface IBod : IVypisovací int X get; set; int Y get; set; event EventHandler ZmenaSouradnic;
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 3 class Sestava : Ivypisovací public void Vypis() Console.WriteLine("Výpis údajů sestavy");
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 1 interface IVypisovací void Vypis(); class A : IVypisovací public void Vypis() Console.WriteLine("Výpis údajů třídy A");
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é. if (o is IVypisovací) iv = (IVypisovací)o; else iv = null; 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.
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 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 2 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();
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 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 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 public interface ICloneable object Clone(); 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.
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 public interface IComparable int CompareTo (object obj); 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.
Předdefinovaná rozhraní IComparer public interface IComparer int Compare(object x, object y); 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.
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; using System.Collections; 1
Předdefinovaná rozhraní IComparer příklad delegate int CompareOsobaCallback(Osoba x, Osoba y); 2 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);
Předdefinovaná rozhraní IComparer příklad delegate int CompareOsobaCallback(Osoba x, Osoba y); 2 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);
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 udává, zda instance třídy ResourceHolder byla již zlikvidována automatická správa paměti -> není už potřeba volat destruktor 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); uvolní neřízené i řízené prostředky s false je volána z destruktoru s true je volána z Dispose() uvolní neřízené prostředky 1
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.
Jednodušší způsob pro implemeentaci rozhraní IEnumerator (IEnumerable). 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 ; Poskytuje následující hodnotu iterace Indikuje, že iterace je kompletní 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.
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 Iterátor může obsahovat i metoda, která má parametry (ne však ref či out). 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();
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ýraz nep ; 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říkazy nep seznam_handlerů nep koncovka try příkazy nep seznam_handlerů Syntaxe handleru catch ( typ identifikátor nep ) 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 1 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);
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() Vytvoří instanci s implicitním textem chyby public Exception(string message) Vytvoří instanci s textem chyby message public Exception(string message, Exception innerexception) Vytvoří instanci s textem chyby message a nastaví vnitřní výjimku
Třídy Třída Exeption Vlastnosti public virtual string Message get; Poskytuje text chyby, který do instance uložil konstruktor public virtual string StackTrace get; Ř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í) public MethodBase TargetSite get; Poskytuje informace o, která výjimku vyvolala. public virtual string Source get; set; Poskytuje/nastavuje název aplikace nebo objektu, který výjimku způsobil
Třídy Třída Exeption Vlastnosti public virtual string HelpLink get; set; Poskytuje/nastavuje název souboru nápovědy, v němž jsou další informace o dané výjimce. public virtual IDictionary Data get; Poskytuje uživatelská data o výjimce uložená ve slovníku. public Exception InnerException get; 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 Vznikne při dělení nulou Vznikne při přetečení celočíselného typu, je-li kontrolováno System.IndexOutOfRangeException - pokud index prvku pole je mimo meze tohoto pole.
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 ==!= < > <= >= true false 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 +=.
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í. 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. 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.
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 2 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();
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 1 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();
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 1 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
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 Open Truncate Create Popis Otevření existujícího souboru Otevření existujícího souboru a vymazání jeho obsahu 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 Append Otevření existujícího souboru. Vytvoří se nový, pokud ten neexistuje. Otevření souboru a přesunutí ukazatele na jeho konec
Soubory FileAccess Konstanta Read Write ReadWrite Popis Povoluje čtení ze souboru Povoluje zápis do souboru Povoluje čtení i zápis z/do souboru
Soubory FileShare Konstanta None Read Write ReadWrite Delete Popis Zakazuje sdílení Povoluje čtení ze sdíleného souboru Povoluje zápis do sdíleného souboru Povoluje čtení i zápis z/do sdíleného souboru 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); 1 foreach (string adr in adresare) cesta = NajdiSouborVAdresari(soubor, adr); if (cesta!= null) return cesta; catch return null; return null;
Příklad 2 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();
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(); 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(); 2 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 1 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);
Č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 1 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);
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 1 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();
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.SerializationEx ception. 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.Bina ry.binaryformatter serializace v binárním formátu, System.Runtime.Serialization.Formatters.So ap.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 2 [Serializable] class C B[] b; public C(B[] b) this.b = b; public void Vypis() foreach (B x in b) Console.WriteLine(x.ToString());
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(); 3 Console.ReadLine();
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"); 2 3 public void GetObjectData(SerializationInfo info, StreamingContext context) info.addvalue("obor", obor); info.addvalue("fakulta", (int)fakulta); public void Vypis() Console.WriteLine("fakulta: " + fakulta + ", " + obor);
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 1 class Pocitadlo private int pocet; public int Pocet get return pocet; set pocet = value;
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 1 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;
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 3 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());
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 C nebo c D nebo d E nebo e F nebo f G nebo g Význam 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. Čí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. 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. 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. 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 N nebo n P nebo p R nebo r X nebo x Význam Čí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. Čí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. 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. 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 d D f Význam Zkrácený formát data podle vzoru vlastnosti ShortDatePattern. Dlouhý formát data podle vzoru vlastnosti LongDatePattern. 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 G M nebo m o R nebo r s Kombinace specifikátoru d a t oddělená mezerou. Kombinace specifikátoru d a T oddělená mezerou. Formát data podle vzoru vlastnosti MonthDayPattern. 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. 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. 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 t T u U Y nebo y Význam Zkrácený formát času podle vzoru vlastnosti ShortTimePattern. Dlouhý formát času podle vzoru vlastnosti LongTimePattern. 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. 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). 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 o 20 ledna 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 R s Výstup Sat, 20 Jan 2007 23:05:10 GMT 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 d dd ddd dddd f, ff,, fffffff F, FF,, FFFFFFF Význam Den v měsíci v rozsahu 1 až 31 bez nuly na začátku. Den v měsíci v rozsahu 01 až 31 s nulou na začátku. Zkrácený název dne v týdnu daný vlastností AbbreviatedDayNames. Úplný název dne v týdnu daný vlastností DayNames. Desetiny (f), setiny (ff), tisíciny (fff) atd. sekund se zobrazením koncových nevýznamných nul. 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 g h hh H HH K Význam Zkratka éry. Pro český kalendář (gregoriánský) se jedná o zkratku n. l. (našeho letopočtu). Hodiny v rozsahu 1 až 12 bez nuly na začátku. Hodiny v rozsahu 01 až 12 s nulou na začátku. Hodiny v rozsahu 0 až 23 bez nuly na začátku. Hodiny v rozsahu 0 až 23 s nulou na začátku. 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 m mm M MM MMM MMMM Význam Minuty v rozsahu 0 až 59 bez nuly na začátku. Minuty v rozsahu 00 až 59 s nulou na začátku. Měsíc v rozsahu 1 až 12 bez nuly na začátku. Měsíc v rozsahu 01 až 12 bez nuly na začátku. Zkrácený název měsíce daný vlastností AbbreviatedMonthNames. Ú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 ss Sekundy v rozsahu 0 až 59 bez nuly na začátku. Sekundy v rozsahu 00 až 59 s nulou na začátku. t tt y yy Reprezentuje první znak zkratky A.M./P.M. (v aktuální kultuře) daný vlastností AMDesignator a PMDesignator. Reprezentuje zkratku A.M/P.M. (v aktuální kultuře) daný vlastností AMDesignator a PMDesignator. Dvouciferný rok bez nuly na začátku. 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 G/g F/f D/d X/x Význam 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. 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. Hodnota se převede na celé číslo v desítkové soustavě. 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