7. Referenciák Típusai, Típuskonverziók, Operátorok és Névterek
A korábbiakban megtanultuk, hogy egy osztály öröklődhet egy másiktól, és a leszármazott mindent tud, amit az ős. Ebből az alapelvből következik az objektumorientált programozás egyik legerősebb szabálya: A leszármazott mindig helyettesítheti az őst.
Ebből a fejezetből megtudhatod, hogyan kezeli a fordító a memóriában lévő objektumokat, és miként hivatkozhatsz rájuk dinamikusan.
1. A referencia változó két típusa
Mikor deklarálunk egy referenciaváltozót (például Jarmu auto = new Szemelyauto();), a változónak valójában két típusa van:
-
Statikus típus: Az a típus, amit a deklarációkor megadtunk a bal oldalon (a fenti példában ez a
Jarmu). A fordító kizárólag a statikus típus alapján dönti el, hogy egy referencián keresztül milyen metódusokat hívhatunk meg. Tehát csak aJarmuosztályban (és őseiben) definiált metódusokat használhatjuk. -
Dinamikus típus: Az a típus, amilyen objektum ténylegesen létrejött a memóriában a jobb oldalon (a fenti példában ez a
Szemelyauto). Ez mindig megegyezik a statikus típussal, vagy annak egy leszármazottja.
Következmény: Mivel minden osztály az Object osztályból származik, egy Object típusú referenciaváltozó (mint statikus típus) az égvilágon bármilyen objektumra mutathat (mint dinamikus típus).
2. Dinamikus típusvizsgálat és Típuskonverziók (C#)
Sokszor futási időben (dinamikusan) kell ellenőriznünk, hogy egy ősosztályú referencia éppen milyen leszármazott objektumra mutat.
A C# ehhez biztosítja az is operátort, amely egy logikai (igaz/hamis) értéket ad vissza, ha a referencia dinamikus típusa megegyezik a megadott típussal (vagy annak leszármazottjával). Ezen felül a pontos egyezés vizsgálatára használható a typeof operátor kombinálva a .GetType() metódussal.
Ha biztosak vagyunk benne, hogy a referencia a megfelelő objektumra mutat, a metódusainak eléréséhez konvertálni (castolni) kell a referenciát:
-
Explicit típuskonverzió: A C stílusú
(Tipus)valtozoformátum. Ha a konverzió nem hajtható végre, a program futási idejű hibával elszáll. -
Az
asoperátor: Biztonságos konverzió. Megpróbálja átkonvertálni a referenciát a kért típusra; ha ez nem lehetséges, hibadobás helyett egyszerűennullértéket ad vissza.
3. Operátorok Kiterjesztése (Operator Overloading)
A C# nyelvben az operátorok kiterjesztése a statikus polimorfizmus egy elegáns megvalósítása. Segítségével megtaníthatjuk a saját osztályainkat (például egy Tort osztályt) arra, hogy értelmezni tudják a hagyományos matematikai operátorokat (például +, -, *, /).
-
Az operátorok valójában
public staticmetódusként vannak megvalósítva. -
A szintaktikája:
public static Visszateres operator +(Tipus a, Tipus b). -
Fontos szabály, hogy az operátorok precedenciája (sorrendje) és az operandusok száma nem módosítható.
-
Ezzel a módszerrel definiálhatunk a saját típusainkhoz implicit és explicit típuskonverziós (cast) operátorokat is.
4. Névterek és Csoportosítás (C#)
Nagyobb programok (projektek) esetén elkerülhetetlen, hogy különböző osztályoknak (például egy gombnak és egy ruhagombnak) ugyanaz legyen a neve. Ennek megelőzésére (a névütközések elkerülésére) a C# névtereket (namespace) használ.
-
A névtér egy logikai egység, amely önálló hatáskört definiál, ezen belül az azonosítóknak egyedinek kell lenniük.
-
Ha nem definiálunk névteret, a kódunk a "név nélküli" (globális) névtérbe kerül.
-
Más névterek tartalmát a
usingkulcsszóval tehetjük elérhetővé a kódunk számára (példáulusing System;). Ekkor elegendő az osztály rövid nevét használni (a teljes, ponttal elválasztott hosszú név helyett). -
Ha még így is névütközés lépne fel (mert több importált névtérben is van ugyanakkora nevű osztály), bevezethetünk aliasokat is:
using diag = System.Diagnostics.Tracing;.
Gyakorló feladat
Készíts egy programot, amely az operátorok kiterjesztését és a biztonságos típuskonverziót (as operátor) gyakoroltatja!
-
Hozz létre egy
Pontosztályt két (xésy) egész típusú adattaggal! -
Definiáld felül az
+operátort úgy, hogy kétPontobjektum összeadásakor a koordináták összeadódjanak, és egy újPontobjektumot adjon vissza. -
Készíts egy
ToString()felüldefiniálást az ellenőrzéshez! -
A
Mainmetódusban adj össze két pontot! Ezt az eredményt egyObjecttípusú referenciaváltozóba mentsd el, majd azasoperátor segítségével konvertáld visszaPonttípusúra, és csak sikeres konverzió esetén írasd ki a képernyőre!
A feladat megoldása
using System;
class Pont
{
public int X { get; set; }
public int Y { get; set; }
public Pont(int x, int y)
{
this.X = x;
this.Y = y;
}
// 2. Operátor kiterjesztés (Túlterhelés)
public static Pont operator +(Pont a, Pont b)
{
return new Pont(a.X + b.X, a.Y + b.Y);
}
// 3. ToString() felülírása a formázott kiíráshoz
public override string ToString()
{
return $"({this.X}, {this.Y})";
}
}
class Program
{
static void Main(string[] args)
{
Pont p1 = new Pont(2, 3);
Pont p2 = new Pont(5, 7);
// Összeadás, amit egy Object típusba rakunk (ősbe)
Object eredmenyObj = p1 + p2;
// 4. Visszakonvertálás az 'as' operátorral
Pont konvertaltPont = eredmenyObj as Pont;
if (konvertaltPont != null)
{
Console.WriteLine($"A két pont összege: {konvertaltPont.ToString()}");
}
else
{
Console.WriteLine("A típuskonverzió sikertelen volt.");
}
Console.ReadKey();
}
}