Az öröklődés az objektumorientált nyelvek esetén a polimorfizmus megvalósításának egyik legfontosabb módszere. Az öröklődés lényege, hogy a kódunkat újra fel tudjuk használni.
Az öröklődés használata rendkívül leegyszerűsíti a gyakran használt metódusok, adatok integrálását az egyes osztályokba anélkül, hogy meg kellene változtatni az osztályok struktúráját, valamint segíti a karbantarthatóságot és felgyorsítja a fejlesztést.
C# esetén minden osztály csak egy ősosztállyal rendelkezhet, míg más nyelvek esetén, mint például a C++, egy osztálynak több ősosztálya is lehet. A modern OOP nyelvek esetén több okból kifolyólag azonban az ősök számát egyre korlátozták.
Az öröklés szintaxisa C# esetén igen egyszerű. Az osztály neve után kettősponttal meg kell adni, hogy melyik osztályból származik az új osztályunk:
class pelda: object
{
}
Minden osztály implicit módon az object osztályból származik, így a fenti példának nincs sok értelme.
Előfordulhat, hogy az osztályunk öröklési láncban való részvételét valamilyen okból kifolyólag korlátozni szeretnénk. Ebben az esetben használhatjuk a sealed kulcsszót. Ha egy osztályt a sealed módosítóval látunk el, akkor az az osztály további osztályok őse nem lehet. A korábban tárgyalt static módosító osztályok esetén magában hordozza a sealed kulcsszó jelentését, vagyis statikusként megjelölt osztályokból sem örököltethetünk.
sealed class pelda: object
{
}
//fordítási hiba lesz, mivel a pelda osztály zárt!
class teszt: pelda
{
}
Az osztályok között egy speciális fajta az absztrakt osztály. Az absztrakt osztály olyan, ami tipikusan öröklési lánc őse szokott lenni. Az absztrakt osztályok közvetlenül nem példányosíthatóak, viszont tetszőleges kódot, tulajdonságokat tartalmazhatnak, amelyek későbbiekben felhasználhatóak.
Az absztrakt osztályok rendelkezhetnek absztrakt metódusokkal. Az absztrakt metódus egy olyan metódus, aminek csak a paraméter listáját és nevét definiáljuk, de a konkrét megvalósítását nem. Ez akkor jöhet jól, ha a metódus megvalósításának osztályonként eltérőnek kell lennie.
abstract class Pelda
{
public abstract void Valami();
}
Az absztrakt metódusok megvalósítása a leszármazott osztályokban szintén az override kulcsszó segítségével történik, mint a virtuális függvények esetén. A fő különbség, hogy a virtuális függvények felüldefiniálhatóak, de nem kötelezően. Ezzel szemben az absztrakt metódusok megvalósítása kötelező a leszármaztatott osztályokban is.
class Orokolt : Pelda
{
public override void Valami()
{
//a valami függvény implementációja
}
}
Ha a leszármazott osztály maga is absztakt, akkor nem kötelező az absztakt metódus implementálása:
abstract class Masik : Pelda
{
//nem kell implementálni, csak abban az osztályban
//ami ebből fog származni és nem absztrakt.
}
Amennyiben az implementáció hiányozna, akkor az IntelliSense hibát fog jelezni, illetve a programunk nem fog lefordulni.
Számos absztrakt metódussal rendelkező osztály esetén az implementáció sablonja könnyen legenerálható a Visual Studio beépített eszközeivel. Az absztrakt osztályt implementáló osztály nevén jobb kattintva a Quick Actions menüt választva az opciók között fel fog bukkanni egy Implement Abstract Class menüpont.
Erre kattintva a szerkesztő legenerálja az összes definiálandó absztrakt metódus sablonját. Alapértelmezetten a generált kód fordítható, viszont az osztály használata nem ajánlott, mivel a generált kódban minden metódus és tulajdonság használata NotImplementedException-t vált ki.
Természetesen ezen kivétel dobásokat kell helyettesítenünk a saját kódunkkal.
Az osztályok tervezésének több módszere is létezik. A legkézenfekvőbb a manuális kódolásos megoldás.
Az öröklődés hasznosságának bemutatására a legkézenfekvőbb egy példát nézni. Számos könyv ilyenkor egy matematikai, azon belül is geometriai példát hoz fel. Próbáltam saját, egyedi megoldással előállni, azonban nem könnyű olyan témát találni, ami hasonlítható a kézzel fogható világhoz, így maradtam én is a geometriai példánál.
A feladat az lenne, hogy készítsünk egy olyan programot, amely képes a képernyőre kirajzolni négyzetet, téglalapot, illetve tartalmaz bővítési lehetőséget későbbi alakzatok számára.
A kód tervezésének első lépéseként alkottam egy absztrakt ősosztályt, amit nemes egyszerűséggel Ososztaly-nak neveztem el, bár az alakzat szó helytállóbb lett volna.
A képernyő konzol esetén 80×25 karakterből áll, vagyis van egy szélességünk és egy magasságunk. Alakzat rajzolásokhoz pedig nem árt tudni, hogy honnan kezdve rajzoljuk azt az alakzatot, ezért felvettem két tulajdonságot, amelyeket X és Y névvel láttam el. Ezek fogják tárolni az alakzat bal felső pontjának a koordinátáját. Ezek csak olvasható tulajdonságok, viszont az Ososztaly által írhatóak is.
Az írást a konstruktor végzi el. Átvesz egy x és y paramétert, amit a megfelelő tulajdonságokban tárol.
Ezen felül ez az osztály rendelkezik még egy absztrakt metódussal, amit Rajzol-nak neveztem el. Ezen metódus szerepe az lesz, hogy a képernyőre rajzolja az adott alakzatot a leszármaztatott osztályokban. Az absztrakt definíció azért szükséges, mert egy négyzet rajzolása igencsak eltér például a kör rajzolásától.
Ezután implementáltam a Teglalap osztályt, ami az Ososztaly leszármazottja. A Teglalap osztály két további tulajdonsággal bővíti a tulajdonságok listáját, amelyek a Szelesseg és a Magassag. Ezek szintén olvashatóak és szintén csak az osztályon belül írhatóak.
Az osztály konstruktora meghívja az ősosztály kétparaméteres konstruktorát, majd ezeknek átadja a konstruktor által kapott x és y értéket, így azok beállítódnak. A paraméter szélesség és magasság tárolása a tulajdonságok beállításával történik.
using System;
namespace PeldaOroklodes
{
//absztrakt ősosztály. A többi objektum őse
public abstract class Ososztaly
{
//x és y koordináták, tulajdonságok, amiket csak olvasni lehet
//viszont az ősosztály írhatja is őket
public int X { get; private set; }
public int Y { get; private set; }
//absztrakt rajzoló függvény, minden leszármazott osztálynak meg kell valósítania
public abstract void Rajzol();
//konstruktor x és y koordináták beállítására
public Ososztaly(int x, int y)
{
X = x;
Y = y;
}
}
//teglalap, ami az ősosztályból származik.
//definiálja a Rajzol metódust, illetve két új tulajdonságot is
public class Teglalap : Ososztaly
{
//Téglalap méretetét beállító tulajdonságok
public int Szelesseg { get; private set; }
public int Magassag { get; private set; }
//konstruktor, ami az ősosztály konstruktorát is hívja
public Teglalap(int x, int y, int szele, int magassag) : base(x, y)
{
Szelesseg = szele;
Magassag = magassag;
}
//Rajzol függvény megvalósítása
public override void Rajzol()
{
Console.SetCursorPosition(X, Y);
for (int i = 0; i < Magassag; i++)
{
for (int j = 0; j < Szelesseg; j++)
{
if (i == 0 || i == Magassag-1) Console.Write("-");
else
{
if (j == 0 || j==Szelesseg-1) Console.Write("|");
else Console.Write(" ");
}
}
Console.WriteLine();
}
}
}
//A négyzet megvalósítása
public sealed class Negyzet: Teglalap
{
public Negyzet(int x, int y, int meret) : base(x, y, meret, meret) { }
}
}
A kód igazi lényegi része a Rajzol metódus. Ez első körben a megadott x és y koordinátára helyezi a kurzort, ezután pedig egymásba ágyazott for ciklusok segítségével megtörténik a téglalap rajzolása. A kódban a különböző osztályaink Rajzol() metódusa dinamikus polimorfizmust valósít meg, mivel a típus alapján, futás időben fog eldőlni, hogy melyik implementációt kell meghívni.
Az öröklés zsenialitása, hasznossága a Negyzet osztály implementációjában mutatkozik meg. A négyzet felfogható egy olyan téglalapnak, aminek a magassága és szélessége azonos.
Ebből adódóan kézenfekvő megoldás a Teglalap osztályból származtatni. Továbbá, mivel a Negyzet osztályból nem tudunk más geometriai alakzatot származtatni, amit hasonló módon lehetne értelmezni, ezért célszerű itt lezárni az öröklési láncot a sealed kulcsszóval.
A Negyzet konstruktora három paraméteres. Kell az x, y koordináta és a méret. A konstruktor az ősosztály konstruktorát hívja, ami az x, y koordinátákat beállítja, illetve a szélességnek és magasságnak pedig a méret paramétert adja át.
A Rajzol metódust ebben az esetben nem kell felüldefiniálni (bár megtehetnénk, mivel az absztrakt metódusok bármelyik örököltetett osztályból felüldefiniálhatóak), mivel ugyanúgy történik a rajzolás.
A fő program ezután "csak" használatba veszi az osztályokat:
using System;
namespace PeldaOroklodes
{
class Program
{
static void Main(string[] args)
{
Teglalap t = new Teglalap(0, 0, 12, 10);
Negyzet n = new Negyzet(0, 11, 6);
t.Rajzol();
n.Rajzol();
Console.ReadKey();
}
}
}
A program kimenete:
------------
| |
| |
| |
| |
| |
| |
| |
| |
------------
------
| |
| |
| |
| |
------
2025.11.13. @ 08:28
Szuper!