PŘETĚŽOVÁNÍ OPERÁTORŮ



Podobné dokumenty
TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

Fakulta elektrotechniky a informatiky Univerzita Pardubice 2014/2015. poslední přednáška a materiál k samostudiu

Jazyk C++ I. Polymorfismus

Přetěžování operátorů

Úvod do programovacích jazyků (Java)

PREPROCESOR POKRAČOVÁNÍ

Generické programování

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

PB161 Programování v jazyce C++ Přednáška 9

přetížení operátorů (o)

typová konverze typová inference

Zápis programu v jazyce C#

SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

Ukazka knihy z internetoveho knihkupectvi

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Úvod do programovacích jazyků (Java)

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members)

Úvod Přetěžování Generika Kolekce Konec. Programování v C# Další jazykové konstrukce. Petr Vaněček 1 / 31

Jazyk C++ I. Šablony 2

Jazyk C# (seminář 6)

5 Přehled operátorů, příkazy, přetypování

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/

Mnohotvarost (polymorfizmus)

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Základy jazyka C# Obsah přednášky. Architektura.NET Historie Vlastnosti jazyka C# Datové typy Příkazy Prostory jmen Třídy, rozhraní

Algoritmizace a programování

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Obsah. Úvod 11 Základy programování 11 Objektový přístup 11 Procvičování 11 Zvláštní odstavce 12 Zpětná vazba od čtenářů 12 Errata 13

Paměť počítače. alg2 1

Jazyk C# (seminář 5)

Jazyk C# (seminář 3)

DATOVÉ TYPY POKRAČOVÁNÍ

Datové typy strana 29

Seminář Java II p.1/43

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

IAJCE Přednáška č. 8. double tprumer = (t1 + t2 + t3 + t4 + t5 + t6 + t7) / 7; Console.Write("\nPrumerna teplota je {0}", tprumer);

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Výčtový typ strana 67

Úvod do programování - Java. Cvičení č.4

C++ přetěžování funkcí a operátorů. Jan Hnilica Počítačové modelování 19

8 Třídy, objekty, metody, předávání argumentů metod

Jazyk C++ 1. Blok 3 Objektové typy jazyka C++ Třída. Studijní cíl. Doba nutná k nastudování. Průvodce studiem

Objekty v PHP 5.x. This is an object-oriented system. If we change anything, the users object.

Mělká a hluboká kopie

Konstruktory a destruktory

4. ZÁKLADNÍ POJMY Z OBJEKTOVĚ ORIENTOVANÉHO PROGRAMOVÁNÍ

Programování v C++ 2, 4. cvičení

IRAE 07/08 Přednáška č. 1

Teoretické minimum z PJV

Předmluva k aktuálnímu vydání Úvod k prvnímu vydání z roku Typografické a syntaktické konvence... 20

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

9. přednáška - třídy, objekty

Seminář Java IV p.1/38

Logické operace. Datový typ bool. Relační operátory. Logické operátory. IAJCE Přednáška č. 3. může nabýt hodnot: o true o false

Základy objektové orientace I. Únor 2010

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

Jazyky C a C++ kompletní průvodce 2., aktualizované vydání. Miroslav Virius

1. Dědičnost a polymorfismus

Programování v C++ 1, 5. cvičení

Objekt. základní prvek v OOP. má vlastnosti. má metody. vznikne vytvoření nové instance definován pomocí třídy

IRAE 07/08 Přednáška č. 2. atr1 atr2. atr1 atr2 -33

Programování v C++ 1, 6. cvičení

Algoritmizace a programování

Chování konstruktorů a destruktorů při dědění

IAJCE Přednáška č. 7. řízení semaforu na křižovatce = přepínání červená/oranžová/zelená

Programování v jazyce JavaScript

IAJCE Přednáška č. 6. logický celek, řešící dílčí část problému Příklad velmi špatného zápisu programu na výpočet obsahu obdélníku

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

přetížení operátorů (o)

Jazyk C++ I. Šablony

Virtuální metody - polymorfizmus

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Objektově orientované programování

Operátory, výrazy. Tomáš Pitner, upravil Marek Šabo

Přetěžování operátorů

1. Programování proti rozhraní

Java Enum Java, zimní semestr ,2017 1

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

Z. Kotala, P. Toman: Java ( Obsah )

Simulace. Martin Pergel

Dědění, polymorfismus

Abstraktní datové typy: zásobník

PB161 Programování v jazyce C++ Přednáška 4

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

Více o konstruktorech a destruktorech

Pokud neuvedeme override, vznikne v synu nová (nevirtuální) metoda (a pochopitelně se nezavolá, jak bychom

Programování v C++ 1, 1. cvičení

Programování v C++, 2. cvičení

SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ

Operátory. Základy programování 1 Martin Kauer (Tomáš Kühr)

3. Třídy. Základní pojmy objektového programování. Třídy

Kolekce, cyklus foreach

URČITÝM ZPŮSOBEM PODOBNÉ

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

NPRG031 Programování II 1 / :25:46

NMIN201 Objektově orientované programování 1 / :36:09

Transkript:

PŘETĚŽOVÁNÍ OPERÁTORŮ Jazyk C# podobně jako jazyk C++ umožňuje přetěžovat operátory, tj. rozšířit definice některých standardních operátorů na uživatelem definované typy (třídy a struktury). Stejně jako v C++ při přetěžování nelze změnit prioritu nebo asociativitu operátoru nebo počet jeho operandů. Nelze také zavést nový operátor. Základní rozdíly mezi C# a C++ jsou následující: Všechny operátory s výjimkou operátoru indexování se musí deklarovat jako statické veřejné metody tříd nebo struktur. Množina přetěžovaných operátorů je podstatně menší než v C++. Přetížené operátory nelze volat zápisem operátorové funkce. Pravidla pro přetěžování operátorů Operátory, které lze přetížit Jazyk C# rozlišuje čtyři skupiny operátorů, které lze přetěžovat: unární operátory, binární operátory, konverzní operátory, indexery (operátory indexování). Pro indexer platí odlišná pravidla, než pro ostatní skupiny operátorů. Jeho deklarace se v C# nepovažuje za přetížení operátoru, nicméně je také popsán v této kapitole. V C# lze přetěžovat následující unární a binární operátory: unární operátory: +! ~ ++ -- true false binární operátory: + - * / % & ^ << >> ==!= > < >= <= Klíčová slova true a false se pro účely přetěžování operátorů považují za unární operátory. Unární a binární operátory Syntaxe: deklarace přetíženého operátoru: public static typ operator symbol ( seznam_parametrů ) tělo static public typ operator symbol ( seznam_parametrů ) tělo Typ, seznam parametrů a tělo mají stejný význam jako v deklaraci jiné metody. Symbol symbol operátoru. Unární operátor musí mít jeden parametr, binární dva parametry. Tyto parametry odpovídají operandům operátoru. U binárního operátoru první parametr odpovídá levému operandu a druhý parametr pravému operandu. Alespoň jeden z parametrů přetíženého operátoru deklarovaného v třídě nebo struktuře typu T, musí mít parametr typu T nebo T?. Operátory bitového posunu >> a << musí mít první parametr typu T nebo T? a druhý parametr typu int nebo int?. Parametry přetížených operátorů se smějí 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. 1

Některé operátory tvoří logické dvojice. To znamená, že pokud se přetíží jeden z nich, musí se přetížit i druhý. Jedná se o tyto dvojice operátorů: ==!= < > <= >= 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 +=. Operátory a && také nelze přetěžovat. Nicméně překladač odvozuje význam operátoru od významu operátoru a význam operátoru && od významu operátoru &. Ovšem operátory a && lze používat pouze v případě, že oba operandy a návratový typ přetíženého operátoru a & jsou stejného typu. Je definována třída Matice, zapouzdřující matici celých čísel. 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(); 2

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; static void Main() Matice matice = Matice.Generuj(2, 3); matice.vypis(); matice *= 10; // #3 matice.vypis(); matice = 10 * matice; #4 matice.vypis(); Třída Matice obsahuje operátor násobení matice celým číslem #1. Tento operátor nejprve vytvoří novou instanci matice, mající stejnou velikost jako matice m, která reprezentuje levý operand tohoto operátoru. Potom vynásobí každý prvek matice m zadaným celým číslem a tyto výsledky uloží do nové matice. Nakonec vrátí novou matici. Matice m se nezmění. Aby bylo možné provést násobení s prohozenými operandy, tj. levým operandem by bylo číslo a pravým operandem matice, obsahuje třída Matice ještě jeden operátor násobení #2, který pouze zavolá operátor #1. V příkazu #3 se volá operátor #1 a v příkazu #4 operátor #2. Výpis programu může být např. následující: 96 47 92 71 59 14 960 470 920 710 590 140 9600 4700 9200 7100 5900 1400 Rovná se, nerovná se Pro zjištění rovnosti dvou proměnných lze použít nejen operátory == a!=, ale i tři metody třídy object. Třída object obsahuje statickou a virtuální metodu Equals a dále statickou metodu ReferenceEquals. Jejich význam a význam standardních (nepřetížených) operátorů == a!= se liší pro referenční a hodnotové typy. Pro třídy (referenční typy) je jejich význam následující: Metoda ReferenceEquals vrací true, pokud se obě reference odkazují na stejnou instanci nebo mají-li obě hodnotu null. 3

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. Pro struktury (hodnotové typy) mají následující význam: Metoda ReferenceEquals vždy vrací false. 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 operátor == a metodu Equals Pokud se v třídě nebo struktuře předefinuje metoda Equals, měla by se předefinovat i metoda GetHashCode třídy object. Překladač jinak vypíše varování. Pokud se v třídě nebo struktuře přetíží operátor ==, měla by se předefinovat i metoda Equals. Překladač jinak vypíše 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. 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; 4

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); 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 Příkaz #1 volá přetížený operátor == struktury C, který volá metodu Equals třídy object. Ta volá metodu Equals pro všechny datové složky struktury. Třída B má předefinovanou metodu Equals a proto příkaz #1 vypíše hodnotu True. Pokud by v třídě B nebyla metoda Equals předefinována, příkaz #1 by vypsal hodnotu False. Pokud by ve struktuře C nebyl operátor == přetížen, příkaz #1 by překladač označil za chybný. V příkazu #2 se porovnávají reference na instance dvou tříd pomocí standardního operátoru ==, která porovnává odkazy na tyto instance a tudíž tento příkaz vypíše hodnotu False. Pokud by se přetížil operátor == pro třídu B tak, že by volal statickou metodu Equals public static bool operator ==(B left, B right) return Equals(left, right); příkaz #2 by vypsal hodnotu True. Překladač u tohoto programu vypíše následující varování: Pokud se předefinuje metoda Equals v třídě B, měla by se předefinovat i metoda GetHashCode třídy object. Pokud se přetíží operátor == a!= ve struktuře C, měla by se předefinovat i metoda Equals. Operátory inkrementace a dekrementace Operátory inkrementace a dekrementace se přetěžují jednou verzí, která se použije pro prefixovou i postfixovou verzi. Má jeden parametr. Parametr i návratový typ musí být stejného typu. Pro operátor deklarovaný ve třídě nebo struktuře typu T musí být typu T nebo T? (buď jsou oba typu T nebo oba typu T?). Protože parametr musí být předáván hodnotou, lze modifikovat pouze novou instanci, kterou operátor vrací. 5

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(); 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); Výstup programu bude následující: a1 = 11, a2 = 11 a1 = 12, a2 = 11 Operátory true a false Přetížením operátorů true a false pro typ T lze instanci tohoto typu používat jako podmínku v příkazech if, for, while, do a v operátoru podmíněného výrazu?:. Z toho vyplývá, že návratovým typem těchto operátorů musí být typ bool. Pokud se přetíží jeden z těchto operátorů, musí se přetížit i druhý. 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; 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 Program vypíše text "Instance vrací true". Příkazy #1, #2 a #3 jsou chybné. Příkaz #1 vyžaduje implicitní konverzi z typu A na typ bool. To lze zajistit definováním konverzního operátoru (viz dále) pro třídu A. Aby bylo možné použít příkazy #2 a #3, musely by být ve třídě A definovány operátory! a == nebo konverzní operátor. 6

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, Implicitní konverzní operátor lze použít k implicitní konverzi, explicitní konverzní operátor k explicitní konverzi. Syntaxe: deklarace implicitního konverzního operátoru: public static implicit operator cílový_typ ( zdrojový_typ parametr ) tělo static public implicit operator cílový_typ ( zdrojový_typ parametr ) tělo deklarace explicitního konverzního operátoru: 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. Buď zdrojový typ nebo cílový typ musí být shodný s typem, v němž je tento operátor deklarován. To znamená, že konverzní operátor může provést konverzi z typu, v němž je definován nebo konverzi na typ, v němž je definován. Zdrojový ani cílový typ nesmí představovat rozhraní. Zdrojový a cílový typ nesmí být součástí stejné dědické hierarchie, tj. cílový typ nesmí být přímým nebo nepřímým předkem zdrojového typu a naopak. 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. 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); A a = new A(10); int i = a; // #1 a = (A)10; // #2 V třídě A je definován implicitní konverzní operátor z typu A na typ int a explicitní konverzní operátor z typu int na typ A. Přiřazení instance třídy A do proměnné typu int v příkazu #1 je správné a nevyžaduje explicitní přetypování pomocí operátoru (typ). Zatímco opačné přiřazení v příkazu #2 lze provést pouze pomocí operátoru přetypování. 7

Indexery Indexery se deklarují 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: deklarace indexeru: modifikátory nep typ this [ seznam_parametrů ] deklarace_přístupových_metod modifikátory nep typ typ_rozhraní. this [ seznam_parametrů ] deklarace_přístupových_metod deklarace_přístupových_metod: část_get část_set nep část_set část_get nep 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, v níž je indexer definován, implementuje. Uvede se tehdy, pokud se jedná o 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, že seznam musí obsahovat alespoň jeden parametr a musí se jednat o parametry předávané hodnotou (nelze použít modifikátory ref a out). Parametry mohou být libovolného typu. V jedné třídě (struktuře) lze deklarovat i více indexerů lišících se svojí signaturou, která je dána počtem a typem formálních parametrů. Pro deklaraci přístupových metod platí stejná pravidla jako u vlastností. V případě předefinovaného virtuálního indexeru v potomkovi se volá indexer přímého předka zápisem base[e], kde E je seznam skutečných parametrů indexeru. Je definována třída Matice, zapouzdřující matici celých čísel. Pro přístup k prvku matice na zadaném řádku a sloupci je definován indexer. 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; Matice a = new Matice(2, 3); a[0, 0] = 10; Console.WriteLine(a[0, 0]); 8