Osnova přednášky Programové prostředky řízení Úvod do C# II. Členy (Members) Jmenné prostory (Namespaces) Třídy (Classes) Struktury (Structs) Pavel Balda Západočeská univerzita v Plzni, FAV, KKY 2 Členy (Members) Jmenné prostory a typy mají členy. Členy dané entity (objektu, jmenného prostoru) jsou dostupné pomocí kvalifikovaného jména: entita.jméno-člena Jmenný prostor (namespace): členy jsou jmenné prostory a typy deklarované uvnitř něj Struktura (struct): členy deklarované ve struktuře (např. jednoduché typy) a členy zděděné od třídy object Vyjmenovaný typ (enum): deklarované konstanty a členy zděděné z třídy System.Enum Třída (class): deklarované členy v této třídě a členy zděděné od bázové třídy kromě konstruktorů a destruktorů. Rozhraní (interface): deklarované členy v interfacu, členy všech bázových interfaců a členy zděděné od třídy object Pole (array) : členy zděděné z třídy System.Array Delegát (delegate): členy zděděné z třídy System.Delegate 3 Přístup ke členům Přístup ke členům je určen v deklaraci pomocí modifikátorů: public přístup není omezen protected přístup z typu v němž je deklarace a z odvozených typů internal přístup omezený na daný program protected internal přístup omezený na daný program nebo odvozené typy private přístup omezen na typ, v němž je deklarace uvedena Přístup ke členům (default je podtržen) Namespace vždy public, modifikátory se nesmějí uvádět Typy deklarované ve jmenných prostorech public nebo internal Členy tříd všech 5 druhů, default je private Struktury public, internal nebo private Rozhraní vždy public, modifikátory se nesmějí uvádět Vyjmenované typy vždy public, modifikátory se nesmějí uvádět 4 1
Jmenné prostory (namespaces) Kód programů v C# je organizován do jmenných prostorů namespace kvalifik.-identifikátor tělo-jmen.prostoru ; Středník na konci je nepovinný kvalifik.-identifikátor je složen z identifikátorů oddělených tečkami tělo-jmen.prostoru: direktivy-using deklarace-členů-jmen.prostoru Direktivy using i deklarace členů jmenného prostoru jsou nepovinné Deklarace jmenného prostoru se může nacházet v nejvyšší úrovni ve zdrojovém souboru (takový jmenný prostor se stává členem globálního jmenného prostoru) nebo se nachází uvnitř (vnitřní jmenný prostor) jiného jmenného prostoru (vnější jmenný prostor) V obou případech musí být jméno jmenného prostoru jedinečné uvnitř jej obsahujícího jmenného prostoru Všechny direktivy using musí být před deklarací členů jmenného prostoru Direktivy using Direktivy using usnadňují užívání jmenných prostorů a typů v nich definovaných. Existují ve dvou syntaxích: Direktiva pro import typů ze jmenného prostoru using jméno-jmenného-prostoru ; Příklad: using System.Windows.Forms; Direktiva pro náhradní jméno jmenného prostoru nebo typu using identifikátor = jméno-jmenného-prostoru-nebo-typu ; Používá se pro zkrácení odkazu pomocí zvoleného identifikátoru na nějaký jmenný prostor nebo typ: using Mutex = System.Threading.Mutex; 5 6 Jmenné prostory - příklady Členy jmenných prostorů 1. Ekvivalentní deklarace vnořených jmenných prostorů 2. Dvě třídy patřící do stejného plně kvalifikovaného jmenného prostoru 3. Direktiva using neimportuje vnořené jmenné prostory 4. Víceznačné jméno při importu několika jmenných prostorů Příklad 1 namespace N1.N2 class A class B nebo ekvivalentně namespace N1 namespace N2 class A class B Příklad 2 namespace N1.N2 class A namespace N1.N2 class B Příklad 3 namespace N1.N2 class A namespace N3 using N1.N2; class B: A Chyba, při deklaraci N3: namespace N3 using N1; class B: N2.A // N2 neznámý! Příklad 4 namespace N1 class A namespace N2 class A namespace N3 using N1; using N2; class B: A // A víceznačné! Členy jmenných prostorů mohou být vnořené jmenné prostory nebo deklarace typů: Třídy class Struktury struct Rozhraní interface Vyjmenovaného typu enum Delegátu delegate K deklarovaným typům ve jmenných prostorech je přístup public nebo internal, default je internal. Vnořené typy lze deklarovat ve třídách a ve strukturách 7 8 2
Třídy (Classes) Třída je datová struktura, která může obsahovat: Datové členy (konstanty a položky) Členské funkce (metody, vlastnosti, události, operátory, konstruktory a destruktory) Vnořené typy Třídy mohou využívat dědičnost (inheritance) Odvozená třída (derived class) může rozšiřovat a specializovat třídu základní (base class) Deklarace tříd atributy modifikátory-třídy class identifikátor bázetřídy deklarace-členů-třídy ; Atributy (příští přednáška), modifikátory třídy, báze třídy a středník jsou nepovinné Modifikátory tříd Deklarace třídy může obsahovat modifikátory public, protected, internal, private určují dostupnost třídy (viz Přístup ke členům) new přípustný u vnořených tříd. Nová třída skrývá stejnojmennou třídu předka abstract označuje neúplnou třídu, která je určena pouze jako bázová třída Nelze ji přímo instancovat, pokud se o to pokusíme, hlásí se chyba překladu Může obsahovat abstraktní členy Nemůže být uzavřená (sealed) sealed označuje třídu, od které nelze odvozovat nové třídy Používá se pro zabránění neúmyslnému odvozování Nemůže být abstraktní třídou Umožňuje optimalizovat kód (převádět virtuální metody na nevirtuální) 9 10 Specifikace báze třídy Deklarace třídy může obsahovat specifikaci přímé báze třídy, definující bázovou třídu a rozhraní implementovaná třídou báze-třídy : typ-třídy : seznam-typů-rozhraní : typ-třídy, seznam-typů-rozhraní Příklady class A class A: B class B: A class B: C class C: A // Chyba! // Cyklická závislost Členy tříd Členy tříd mohou být konstanty, položky (fields), metody, vlastnosti, události, indexery, operátory, konstruktory, destruktory, statické konstruktory, typy Třída dědí (obsahuje) členy své přímé bázové třídy kromě konstruktorů, destuktorů Dědičnost je transitivní: je-li C odvozena od B a B je odvozena od A, pak C dědí všechny členy B a též všechny členy A. Vnořené typy (nested types) jsou typy deklarované uvnitř tříd a struktur class A class B // třída B je vnořený typ v A static void F() Console.WriteLine("A.B.F"); 11 12 3
Třídy - Konstanty Konstanta je člen reprezentující konstantní hodnotu, která může být vypočtena ve fázi překladu Deklarace konstanty atributy modifikátory-konstant const typ deklararátory-konstant; Atributy a modifikátory konstant jsou nepovinné Modifikátory mohou být new, public, protected, internal, private Deklarátory konstant se oddělují čárkami, každý je ve tvaru: identifikátor = konstantní-výraz Příklad: Ekvivalentní zápis class A class A public const double public const double X = 1.0; X = 1.0, Y = 2.0, Z = 3.0; public const double Y = 2.0; public const double Z = 3.0; Třídy Položky (fields) (1/2) Položka (field) je člen reprezentující proměnnou třídy (objektu) Deklarace položky atributy modifikátory-položek typ deklararátory-proměnných; Atributy a modifikátory položek jsou nepovinné Modifikátory mohou být new, public, protected, internal, private, static, readonly, volatile Deklarátory položek se oddělují čárkami, každý je ve tvaru: identifikátor identifikátor = výraz identifikátor = inicializátor-pole Příklad: Ekvivalentní zápis class A class A public static int public static int X = 1; X = 1, Y, Z = 100; public static Y; public static Z = 100; 13 14 Třídy Položky (fields) (2/2) Statické položky v deklaraci je uveden modifikátor static Nejsou součástí konkrétní instance, existuje jen jedno umístění v paměti bez ohledu na vytvořený počet instancí objektu Položky instancí v deklaraci není uveden modifikátor static Každá instance má vlastní množinu položek instancí své třídy Položky pouze pro čtení deklarovány s modifikátorem readonly Do položek lze přiřazovat hodnoty v deklaraci, v konstruktoru instance pro položky instancí nebo ve statickém konstruktoru pro statické položky. Pokus o přiřazení v jiném kontextu vede k chybě překladu Nestálé (volatile) položky deklarace obsahuje volatile Zakazuje překladači optimalizační techniky vedoucí k změně pořadí instrukcí, které by mohly vést k nepředvídatelným výsledkům u více threadových programů bez synchronizace (např. příkazem lock) Položky volatile musí mít jeden z typů: Referenční typ byte, sbyte, short, ushort, int, uint, char, float nebo bool Vyjmenovaný typ založený na typech byte, sbyte, short, ushort, int, uint 15 Třídy Inicializace položek Deklarace položek mohou obsahovat inicializaci, která odpovídá přiřazovacím příkazům Pro statické položky se provádí při inicializaci dané třídy Pro položky instancí se vykonává při vytváření instance třídy Položky (statické i instancí) jsou inicializovány vždy. Pokud není inicializace uvedena, inicializují se na default hodnoty (vyplněním nulami). Na rozdíl od C++ nemohou být položky nikdy neinicializovány Příklad: using System; class Test static bool b; int i; static void Main() Test t = new Test(); Console.WriteLine("b = 0, i = 1", b, t.i); Vytváří výstup: b = False, i = 0 16 4
Třídy Metody (methods) Metoda je člen implementující výpočet nebo akci s danou třídou (objektem) Deklarace metody hlavička-metody tělo-metody Tělo metody je blok nebo prázdný příkaz Hlavička metody atributy modifikátory-metody návratový-typ jméno-člena ( seznam-formálních-parametrů ) ; Atributy, modifikátory a seznam formálních parametrů jsou nepovinné Modifikátory mohou být new, public, protected, internal, private, static, virtual, sealed, override, abstract, extern Návratový typ je buď název typu nebo void (metoda nic nevrací) Jméno člena identifikátor typ-rozhraní.identifikátor Třídy Parametry metod (1/3) Parametry metod jsou deklarovány v seznamu formálních parametrů pevné-parametry pevné-parametry, pole-parametrů pole-parametrů Pevné parametry tvoří seznam parametrů pevný-parametr oddělených čárkami. Pevný-parametr je definován: atributy modifikátor-parametru typ identifikátor Atributy a modifikátor jsou nepovinné, modifikátor může nabývat jedné ze dvou hodnot: ref nebo out Pole parametrů atributy params typ-pole identifikátor 17 18 Třídy Parametry metod (2/3) Hodnotové parametry deklarovány bez modifikátorů Skutečný parametr při volání metody musí být implicitně konvertovatelný na hodnotový parametr Referenční parametr deklarován s modifikátorem ref Referenční parametr odpovídá stejnému místu v paměti jako proměnná, která je argumentem v okamžiku volání Výstupní parametr deklarován s modifikátorem out Podobný referenčnímu parametru, proměnná předávaná jako out parametr nemusí být inicializována před voláním, v metodě se považuje za nezinicializovanou (jako lokální proměnná) a před návratem z metody musí být přiřazena Příklad using System; class Test static void Swap(ref int x, ref int y) int temp = x; x = y; y = temp; static void Main() int i = 1, j = 2; Swap(ref i, ref j); //Pozor píše se ref (out)! Console.WriteLine("i = 0, j = 1", i, j); 19 Třídy Parametry metod (3/3) Pole parametrů parametr deklarovaný s modifikátorem params Pokud seznam parametrů obsahuje pole parametrů, musí být posledním parametrem a musí být jednodimenzionálním polem. Při volání mohou být argumenty zapsány dvojím způsobem Jedním výrazem, implicitně konvertovatelným na typ v poli parametrů Nulou nebo několika argumenty, kde každý argument je výraz typu implicitně konvertovatelného na typ prvku v poli parametrů Příklad using System; class Test static void F(params int[] args) Console.Write("Array contains 0 elements:", args.length); foreach (int i in args) Console.Write(" 0", i); Console.WriteLine(); static void Main() int[] arr = 1, 2, 3; F(arr); F(10, 20, 30, 40); F(); Tiskne výstupy: Array contains 3 elements: 1 2 3 Array contains 4 elements: 10 20 30 40 Array contains 0 elements: 20 5
Třídy Statické a instanční metody Statické metody deklarovány s modifikátorem static Nepracují s určitou instancí Nesmějí používat this, jinak je hlášena chyba překladu Pokud jsou použity ve tvaru E.M, označuje E jméno třídy a M jméno metody Metody instancí deklarovány bez modifikátoru static Pracují s instancí třídy Mohou používat this Pokud jsou použity ve tvaru E.M, označuje E jméno instance a M jméno metody Třídy Virtuální metody Nevirtuální metody neobsahují ve své deklaraci modifikátor virtual Implementace je shodná ve třídě ve které jsou zavedeny i ve třídách odvozených Virtuální metoda je taková metoda, jejíž deklarace obsahuje modifikátor virtual Implementace virtuální metody může být nahrazena odvozenými třídami Při běhu se podle typu instance rozhoduje, která metoda se zavolá 21 22 Třídy Virtuální metody: Příklad Třídy Override metody Příklad: rozdíl mezi virtuální a nevirtuální metodou using System; class A public void F() Console.WriteLine("A.F"); public virtual void G() Console.WriteLine("A.G"); class B: A new public void F() Console.WriteLine("B.F"); public override void G() Console.WriteLine("B.G"); class Test static void Main() B b = new B(); A a = b; a.f(); b.f(); a.g(); // volá b.g, ne a.g! b.g(); Tiskne výstupy: A.F B.F B.G B.G 23 Override metoda je taková metoda, jejíž deklarace obsahuje modifikátor override Override metoda mění ( přebíjí ) implementaci existující zděděné virtuální metody (viz příklad na předchozí stránce) Naproti tomu virtuální metoda s modifikátorem virtual zavádí novou metodu Příklad pozor na vynechání override! class A public virtual void F() class B: A public virtual void F() // Warning, schování zděděné F() // - dá se odstranit přidáním modifikátoru new 24 6
Třídy Sealed a Abstract metody Použití modifikátoru sealed chrání sealed metody před možným přepsáním v odvozených třídách Je-li použit modifikátor sealed, musí být spolu s ním použit i override Abstraktní metoda používá modifikátor abstract Zavádí novou virtuální metodu, avšak bez implementace, proto tělo metody obsahuje jen středník. Implementaci musí dodat odvozené třídy Příklad nelze volat implementaci abstraktní metody z předka abstract class A public abstract void F(); class B: A public override void F() base.f(); // Chyba! base.f je abstraktní Třídy Externí metody Externí metody používají modifikátor extern Typicky jsou externí metody implementovány v jiném jazyku než C# Tělo metody je prázdné, obsahuje jen středník Modifikátor extern se typicky používá ve spolupráci s atributem DllImport Tím je umožněno, aby byly externí metody implementovány v DLL knihovnách [DllImport( kernel32, SetLastError=true)] static extern bool CreateDirectory(string name, SecurityAttribute sa); 25 26 Třídy Vlastnosti (properties) Vlastnost je člen umožňující přístup k charakteristikám dané třídy (objektu), např. délka řetězce, velikost fontu, apod. Deklarace vlastnosti atributy modifikátory-vlastnosti typ jméno-člena deklarace-akcesorů Atributy a modifikátory jsou nepovinné Modifikátory mohou být new, public, protected, internal, private, static, virtual, sealed, override, abstract, extern Jméno člena identifikátor typ-rozhraní.identifikátor Modifikátory vlastností jsou velmi podobné s modifikátory metod (výše) Týká se modifikátorů static, virtual, sealed, override a abstract 27 Třídy Accessory vlastností Accessor vlastnosti specifikuje příkazy pro čtení a zápis vlastnosti Deklarace accessorů deklarace-accessoru-get deklarace-accessoru-set deklarace-accessoru-set deklarace-accessoru-get Deklarace druhého accessoru je nepovinná Deklaraceaccessoru get Atributy get tělo-accessoru Deklarace accessoru set Atributy set tělo-accessoru V obou deklaracích jsou atributy nepovinné. Tělo accessoru je buď blok nebo ; (středník) Příklad public class Button: Control private string caption; public string Caption get return caption; set if (caption!= value) caption = value; Paint( ); public override void Paint(Graphics g, Rectangle r) // Zde je kód pro vykreslení 28 7
Třídy Události (events) Třídy Události: Příklad Událost je člen umožňující třídě (objektu) poskytovat notifikace (oznámení). Klient může připojit svůj kód k události pomocí tzv. handlerů událostí (event handlers) Deklarace události atributy modifikátory-události event typ deklarátory-proměnných ; atributy modifikátory-události event typ jméno-člena deklarace-accessorů-události Atributy a modifikátory jsou nepovinné Modifikátory mohou být new, public, protected, internal, private, static, virtual, sealed, override, abstract, extern Události deklarované pomocí accessorů události (druhý případ) jsou mimo rámec přednášky Typ události musí být typ delegáta odvozený od třídy System.Delegate (příště) Pro přidání a odstranění handleru události se používají operátory += a -= 29 Příklad ukazuje, jak přidat handlery událostí k třídě Button public delegate void EventHandler(object sender, EventArgs e); public class Button: Control public event EventHandler Click; public class LoginDialog: Form Button OkButton; Button CancelButton; public LoginDialog() OkButton = new Button(...); OkButton.Click += new EventHandler(OkButtonClick); CancelButton = new Button(...); CancelButton.Click += new EventHandler (CancelButtonClick); void OkButtonClick(object sender, EventArgs e) // Handle OkButton.Click event void CancelButtonClick(object sender, EventArgs e) // Handle CancelButton.Click event Použití události vypadá jako by událost byla položka třídy Button 30 Třídy Indexery Třídy Indexery: Příklad Indexer je člen umožňující, aby byl objekt indexován stejným způsobem jako pole (array). Deklarace indexeru atributy modifikátory-indexeru deklarátor-indexeru deklarace-accessorů Atributy a modifikátory jsou nepovinné Modifikátory mohou být new, public, protected, internal, private, virtual, sealed, override, abstract, extern Pro modifikátory platí stejná pravidla jako u metod Deklarace accessorů je shodná s deklarací u vlastností (properties) Deklarátor indexeru typ this [ seznam-formálních-parametrů ] typ-rozhraní. this [ seznam-formálních-parametrů ] Typ v deklarátoru specifikuje typ položky pole Seznam formálních parametrů má stejný tvar jako u metod s výjimkou, že indexer musí mít alespoň jeden parametr a nelze užívat modifikátory ref a out 31 using System; class BitArray int[] bits; int length; public BitArray(int length) if (length < 0) throw new ArgumentException(); bits = new int[((length - 1) >> 5) + 1]; this.length = length; public int Length get return length; public bool this[int index] get if (index < 0 index >= length) throw new IndexOutOfRangeException(); return (bits[index >> 5] & 1 << index)!= 0; set if (index < 0 index >= length) throw new IndexOutOfRangeException(); if (value) bits[index >> 5] = 1 << index; else bits[index >> 5] &= ~(1 << index); 32 8
Třídy Operátory (1/2) Třídy Operátory (2/2) Operátor je člen definující význam operátoru ve výrazu, kde se vyskytuje daná třída Deklarace operátoru atributy modifikátory-operátoru deklarátor-operátoru tělo-operátoru Atributy jsou nepovinné Modifikátory mohou být public, static, extern Tělo operátoru je blok nebo ; (středník) Deklarátor operátoru typ operator přetížitelný-unární-operátor ( typ identifikátor ) typ operator přetížitelný-binární-operátor ( typ identifikátor, typ identifikátor ) implicit operator typ ( typ identifikátor ) explicit operator typ ( typ identifikátor ) Poslední dva případy jsou tzv. konverzní operátory Přetížitelný unární operátor je jedním z operátorů: + -! ~ ++ -- true false Přetížitelný binární operátor je jedním z operátorů: + - * / % & ^ << >> ==!= > < >= <= Pro deklarace operátorů platí následující pravidla Deklarace operátorů musí obsahovat současně modifikátory public a static Parametry operátorů musí být hodnotového typu. Při použití ref a/nebo out parametrů je hlášena chyba překladu Nelze deklarovat dva operátory se stejnou signaturou Binární operátory Musí mít dva parametry, alespoň jeden musí mít typ třídy (struktury), ve které je deklarován Následující binární operátory vyžadují deklaraci po dvojicích operator == a operator!= operator > a operator < operator >= a operator <= 33 34 Třídy Operátory: Příklad public class IntVector public IntVector(int length)... public int Length... // read-only property // read-write indexer public int this[int index]... public static IntVector operator ++(IntVector iv) IntVector temp = new IntVector(iv.Length); for (int i = 0; i < iv.length; i++) temp[i] = iv[i] + 1; return temp; class Test static void Main() IntVector iv1 = new IntVector(4); // vector of 4 x 0 IntVector iv2; iv2 = iv1++; // iv2 contains 4 x 0, iv1 contains 4 x 1 iv2 = ++iv1; // iv2 contains 4 x 2, iv1 contains 4 x 2 Třídy Operátoryis a as Operátor is se používá pro kontrolu, zda typ objektu při běhu (run-time type) po vyhodnocení výrazu e je kompatibilní s daným typem T e is T Je-li e rovno null, vrací false Je-li e typu T (a není null), vrací true Je-li e typu R pak se vrací true pokud existuje implicitní konverze z R na T (R je odvozený od T) int i = 0; char[] s = 'a','h','o','j'; Console.WriteLine("i is object: 0; s is string: 1", i is object, s is string); Vypíše výstup: i is object: True; s is string: False Operátor as se používá pro explicitní konverzi výrazu e na daný referenční typ T e as T Podobný explicitní konverzi výrazu e na typ T, avšak nikdy nezpůsobí výjimku, pokud konverze není možná, vrací null Pokud nelze konverzi nikdy provést, hlásí se chyba překladu V obou operátorech se používají implicitní konverze a zabalování (boxing) Operátory nepodporují uživatelem definované konverze! 35 36 9
Třídy Konstruktory instancí Třídy Statické konstruktory Konstruktor instance je člen, implementující akce nezbytné pro inicializaci instance třídy Deklarace konstruktoru atributy modifikátory-konstruktoru deklarátor-konstruktoru tělo-konstruktoru Atributy a modifikátory jsou nepovinné Modifikátory mohou být public, protected, internal, private, extern Tělo konstruktoru je blok nebo ; (středník) Deklarátor konstruktoru identifikátor ( seznam-formálních-parametrů ) inicializátor-konstruktoru Identifikátor musí být jménem třídy, ve které je konstruktor deklarován Pro seznam formálních parametrů platí stejná pravidla jako u metod Inicializátor konstruktoru je nepovinný. Není-li uveden je to jako : base() Inicializátor konstruktoru : base ( seznam-argumentů ) : this ( seznam-argumentů ) Seznam argumentů je nepovinný Specifikují jiné konstruktory, které budou vykonány předtím, než se vstoupí do těla konstruktoru Konstruktory instancí se nedědí! 37 Statický konstruktor je člen, implementující akce nezbytné pro inicializaci třídy Deklarace statického konstruktoru atributy modifikátory-statického-konstruktoru identifikátor ( ) tělo-statického-konstruktoru Atributy a modifikátory jsou nepovinné Modifikátory mohou být extern static nebo static extern. Modifikátor extern je nepovinný Tělo konstruktoru je blok nebo ; (pro modifikátor extern) Statické konstruktory se nedědí a nelze je volat přímo Statický konstruktor třídy se volá nejvýše jednou v dané aplikaci a je volán při první z následujících činností: Při vytváření instance dané třídy Při volání nějaké statické metody dané třídy 38 Třídy Destruktory Destruktor je člen, implementující akce nezbytné pro zrušení (destrukci) instance dané třídy Deklarace destruktoru atributy extern ~ identifikátor ( ) tělo-destruktoru Atributy a modifikátor extern jsou nepovinné Identifikátor musí být jménem třídy, ve které je destruktor deklarován Tělo konstruktoru je blok nebo ; (pro modifikátor extern) Destruktory se nedědí a nelze je volat explicitně Instance se stává způsobilou pro destrukci, pokud ji nemůže používat žádný kód. Instanci uklízí garbage collector Destruktory nemohou být přetížené (nemají parametry), proto třída může mít nejvýše jeden destruktor Destruktory jsou volány v pořadí od nejvýše odvozené třídy k níže odvozeným třídám 39 10