A konstruktor objektumok esetén egy speciális metódus, amely az objektum létrehozásakor fut le. Szerepe az osztályon belüli változók kezdőértékének beállítása. Minden osztály legalább egy konstruktor metódust tartalmaz. Ha nem definiáljuk ezt a metódust, akkor a fordító generál egyet, ami paraméter nélküli lesz.
Ha az osztály rendelkezik már egy konstruktorral (paraméteres vagy paraméter nélküli), akkor a frodító nem generál az osztályhoz paraméter nélküli konstruktort. Struktúrák esetén a paraméter nélküli alapértelmezett konstruktort minden esetben a fordító fogja generálni. A konstruktorok jellemzői:
- nincs visszatérési értékük (void kulcsszó sem kell eléjük)
- nevük megegyezik az osztály nevével
- fogadhatnak paramétereket
- több is lehet belőlük, viszont paraméterek számában és típusában egyértelműen megkülönböztethetőnek kell lennie két konstruktornak
A hozzáférési szintek konstruktorok esetén szintén szabályozhatóak, viszont, ha csak egy privát vagy védett elérésű konstruktorral rendelkezik az osztályunk, akkor nem lesz példányosítható. Ennek szerepe akkor van, ha az osztály példányosítását nem szeretnénk közvetlenül megengedni. Egyes tervezési minták építenek erre. Ennek a megoldásnak nagyobb szerepe régebbi .NET verziók esetén volt.
Például akkor, amikor egy olyan osztályt szerettünk volna létrehozni, amely csak statikus elérésű metódusokat tartalmaz. Ebben az esetben ugyebár nincs értelme a példányosításnak, ilyenkor jó választás lehet a privát konstruktor. Viszont később erre a célra bevezették a statikus osztály fogalmát. A statikus osztály a static kulcsszóval van kiegészítve. Ez azt jelzi a fordítónak, hogy az osztály nem példányosítható. Belső működésében egyébként a static megjelölésű osztályt úgy fordítja a fordító, hogy annak a konstruktora privát elérésű legyen.
Az alábbi példa a konstruktorok használatát mutatja be egy egyszerű osztályon:
using System;
namespace PeldaKonstruktorok
{
class KonstruktorPelda
{
private int _szam;
//paraméter nélküli konstruktor
public KonstruktorPelda()
{
_szam = 42;
}
//paraméteres konstruktor
public KonstruktorPelda(int szam)
{
_szam = szam;
}
public void Kiir()
{
Console.WriteLine(_szam);
}
}
class Program
{
static void Main(string[] args)
{
var a = new KonstruktorPelda();
var b = new KonstruktorPelda(33);
a.Kiir();
b.Kiir();
Console.ReadKey();
}
}
}
A program kimenete:
42
33
A példában jól látható, hogy az osztályok példányosítása a new kulcsszóval történik, amit az osztály neve követ. Az osztálynév után pedig zárójelben a konstruktornak átadott paraméterek következnek. A jelenlegi példa definiál egy paraméter nélkülit, aminek a meghívásakor az objektum belsejében tárolt szám változó értéke 42 lesz.
A paraméteres változat pedig a paraméterként kapott értéket teszi bele a belső szám változóba. A belső, privát változók neveit én személy szerint alulvonás karakterrel szeretem kezdeni. Ilyen módon azonnal látható az automatikus kód kiegészítésben a változó nevéből a szerepköre.
A konstruktorok hívása egymáshoz láncolható. Ez azt jelenti, hogy nem kell redundáns1 kódot írnunk.
A redundáns kód karbantarthatósági szempontból káros. Ha egy helyen elrontunk valamit a programunkban és azt másoljuk különböző részekre, akkor bizony igen nehéz lesz a hibát konzisztensen kijavítani, mivel többször is szerepel ugyanaz a hibás részlet a programunkban. Az alábbi példakód az előbbi paraméteres konstruktor esetén mutatja be a konstruktorok láncolásának lehetőségét.
using System;
namespace PeldaKonstruktorok2
{
class KonstruktorPelda
{
private int _szam;
//paraméter nélküli konstruktor
public KonstruktorPelda() : this(42)
{
//Ha ide kódot tennénk, akkor az a Paraméteres
//konstruktor futása után futna le
}
//paraméteres konstruktor
public KonstruktorPelda(int szam)
{
_szam = szam;
}
public void Kiir()
{
Console.WriteLine(_szam);
}
}
class Program
{
static void Main(string[] args)
{
var a = new KonstruktorPelda();
var b = new KonstruktorPelda(33);
a.Kiir();
b.Kiir();
Console.ReadKey();
}
}
}
A program kimenete:
42
33
A módosított példa ugyanúgy működik, mint az előző, azonban itt a paraméter nélküli konstruktor lefutáskor meghívja az egy paraméteres konstruktort 42 értékkel, ami beállítja az értéket. A hivatkozás egy új kulcsszóval történik, amely a this. A this kulcsszó egy osztályon belül minden esetben az adott osztályra mutat.
Jelen esetben a példa nem tesz sok hasznosat, mivel igen rövid a konstruktor kód. Viszont előfordulhat olyan eset, hogy a konstruktor kódunk igen hosszúra nyúlik és ráadásul több változatra is szükségünk van. Ezen esetekben igen ajánlott a konstruktor hívások láncolása.
A this kulcsszó használata
Felmerülhet egyébként a kérdés, hogy a this kulcsszót milyen esetekben érdemes használni? A kérdés megválaszolására egy példát mutatnék:
class pelda
{
private int a;
private int b;
pelda(int a, int b)
{
a = a;
b = b;
}
}
Ezen rövid példakód nem teljesen azt csinálja, amit szeretnénk. Nyilvánvalóan azt szeretnénk, hogy a konstruktor a paramétereként kapott a és b számot az objektum belső a és b változójába másolja.
Azonban a konstruktoron belül a változók hatóköre miatt kicsit másként működik a dolog. Mivel az osztály tagváltozóinak azonos a neve az argumentumokban használt változókkal, így a fordító azt fogja feltételezni, hogy a paraméterként kapott a változót egyenlővé kell tennie a paraméterként kapott a változóval. Ez a működés azért következik be, mert a metódusban használt változók névfeloldása a metódusra koncentrálódik.
Az alábbi kód a helyesen működő változat:
class pelda
{
private int a;
private int b;
pelda(int a, int b)
{
this.a = a;
this.b = b;
}
}
A this kulcsszó használatával explicit megmondhatjuk a kódunkban, hogy fordításkor hogyan is értelmezendő az adott kódrészlet.
Tehát a this használata az osztály adattagjainak elérése esetén akkor kötelező, ha az adott hatókörön belül nem egyértelmű a névfeloldás és csak explicit módon jelezhető, hogy mit is szeretnénk. Többek között ezért is hasznos az alábbi kódolási konvenciók betartása:
- A metódusok definíciójában szereplő paraméter változóneveket írjuk kisbetűkkel.
- Az osztály adattagjainak és metódusainak nevét tevepúpos írással írjuk, vagy a privát adattagokat jelöljük nevük kezdetén aláhúzás karakterekkel.
Statikus konstruktor
A statikus osztályok és a nem statikus osztályok is rendelkezhetnek egy statikus konstruktorral. A statikus konstruktor ebben az esetben az osztályunk bármely statikus tagjának első használata előtt fog közvetlenül lefutni. A statikus konstruktorból csak egy lehet, paramétert nem fogadhat. Nem lehet ellátni elérés módosítóval és közvetlenül nem lehet meghívni.
Ha az osztály rendelkezik statikus és nem statikus konstruktorral, akkor a statikus konstruktorban elhelyezett kód a nem statikus konstruktor kódja előtt fog futni, az osztály első példányosításakor egy alkalommal.
Az alábbi példa a statikus konstruktor használatát mutatja be:
using System;
namespace PeldaStaticKonstruktor
{
static class Pelda
{
private static int _szam;
//statikus konstruktor
static Pelda()
{
_szam = 5;
}
public static void TesztMetodus()
{
Console.WriteLine("Még ennyi van hátra: {0}", Szam);
--_szam;
}
}
class Program
{
static void Main(string[] args)
{
for (int i=0; i<5; i++)
{
Pelda.TesztMetodus();
}
Console.ReadKey();
}
}
}
A program kimenete:
Még ennyi van hátra: 5
Még ennyi van hátra: 4
Még ennyi van hátra: 3
Még ennyi van hátra: 2
Még ennyi van hátra: 1
-
új információt nem tartalmazó, ismétlődő↩