fel
le

THIS paraméter

A this (más nyelveken self) kulcsszóval a példányszintű metódusokban (beleértve a példányszintű konstruktorokat is) hivatkozhatunk az aktuális példányra. Más szóval a 'this' hordozza az aktuális példány referenciáját.

A this változó elérhető minden példány szintű metódusban, típusa egyezik az adott osztály típusával, értéke a példány (amelyiknek a példányszintű metódusa fut éppen) referenciája (memóriacíme). Segítségével tudunk saját magunkra (erre a példányra) hivatkozni a nélkül, hogy ismernénk a külső kódbeli változónevét a példánynak.

Ezen this egy láthatatlan paraméterként viselkedik példányszintű metódus esetében. Mintha minden metódus automatikusan megkapná azt egy extra paraméter formájában automatikusan, a hívás helyén megjelölt példány alapján:

class TAuto
{
  protected int aktBenzin;
 
  public void Feltankol(int mennyiseg)
  {
    aktBenzin += mennyiseg;
  }
}

// ... főprogram ...
TAuto a = new TAuto();
TAuto b = new TAuto();
a.Feltankol( 10 );
b.Feltankol( 15 );
 

A fenti kód valójában az alábbi formában létezik (a fordítóprogram automatikusan végzi az átalakítást):

class TAuto
{
  protected int aktBenzin;
 
  public void Feltankol(int mennyiseg, TAuto this)
  {
    this.aktBenzin += mennyiseg;
  }
}

// ... főprogram ...
TAuto a = new TAuto();
TAuto b = new TAuto();
TAuto -> Feltankol( 10, a );
TAuto -> Feltankol( 15, b );
 

Vagyis a példányszintű metódusok esetén a plusz egy paramétert automatikusan írja hozzá a formális paraméterlistához. A this paraméter típusa megegyezik a szóban forgó osztály típusával.

A hívás helyén a példányt, amelyen keresztül a metódust meghívtuk, automatikusan helyezi el az aktuális paraméterlistában, hogy a this paraméter értéke felvehesse annak referenciáját (memóriacímét).

Ha egy példányszintű metódusban valamely példányszintű mezőre hivatkozunk, akkor az elé automatikusan beíródik a this. hivatkozás, jelezvén hogy ez az aktuális példány szóban forgó mezője kell legyen. Ezért lehetséges az, hogy az a.Feltankol(...) esetében az a példány aktBenzin mezője módosul, míg második esetben a b példány megfelelő mezője módosul, a nélkül hogy ezt a metódus belsejében különösebben jeleznünk kellene.

THIS és az osztályszintű metódusok

Ilyen extra paraméter az osztályszintű metódusok (és konstruktor) esetében nyilvánvalóan nincsen, hiszen ezen metódusok hívásához nincs szükség példányokra. A hívásuk osztálynéven keresztül történik meg, vagyis nincs példány, amely ezen extra paraméter értékét definiálhatná az aktuális paraméterlistában.

THIS felhasználása (1)

Mint láthatjuk, a this a háttérben teljesen automatikusan, és logikusan működik, a programozó beavatkozására nincs szükség. Miért fontos akkor ennek ismerete?

Elsősorban azért, mert ez a magyarázata annak (többek között), hogy this nevű paramétert a programozó nem deklarálhat a metódusaihoz. Ilyen nevű paraméter ugyanis már eleve létezik.

Másrészt ha a programozó tudatában van a this létezésének és szerepének, akkor kihasználhatja azt. Elsősorban két esetben szoktuk hasznát venni a this létezésének:

class TAuto
{
  protected int kilometerAllas;
 
  public TAuto(int kilometerAllas)
  {
    // hibás!
    kilometerAllas = kilometerAllas;
  }
}

// ... főprogram ...
TAuto a = new TAuto(0);
 

A fenti kód írásakor a programozó a konstruktor paraméterét ugyanúgy nevezte el, mint a mezőt. A konstruktor belsejében a paraméter értéket próbálja áttölteni a mezőbe, hogy megadja annak kezdőértékét, de ekkor a fordítóprogram már nem fogja érteni, melyik a mező, és melyik a paraméterváltozó név az értékadásban.

Az azonosítók újradeklarálásának szabálya szerint amennyiben egy azonosítót szűkebb érvényességi körrel újradeklarálunk, akkor ezen szűkebb területen ő elfedi a bővebb érvényességi körű azonosítót. A fenti példában a paraméterváltozó szűkebb érvényességi körű mint a mező, ezért a konstruktor belsejében mindkét kilometerAllas azonosító a paraméterváltozót jelöli, vagyis az értékadó utasítás átmásolja a kilometerAllas paraméterváltozó értékét a kilometerAllas paraméterváltozóba. Ez nyilván "nem csinál semmit", értelmetlen és haszontalan, mindössze pár órajelet vesz el a processzortól.

Nyilvánvalóan a hibát a programozó követte el, aki ugyanúgy nevezte el a paraméterváltozót, mint a mezőt. Nyilvánvalóan a problémát megoldja az, ha másképp nevezi el a paraméterváltozót, mint a mezőt:

class TAuto
{
  protected int fKilometerAllas;
 
  public TAuto(int kilometerAllas)
  {
    // helyes
    fKilometerAllas = kilometerAllas;
  }
}

// ... főprogram ...
TAuto a = new TAuto(0);
 

A fenti névadási szokás egy időben jellemző volt a programozókra, a mezők nevét f-el kezdték (f = field = mező). Ugyanakkor nincs szükség erre, ha a programozó ismeri és felhasználja a this paramétert, amely már a példányszintű konstruktorok esetén is működik:

class TAuto
{
  protected int kilometerAllas;
 
  public TAuto(int kilometerAllas)
  {
    // szintén helyes
    this.kilometerAllas = kilometerAllas;
  }
}

// ... főprogram ...
TAuto a = new TAuto(0);
 

A fenti megoldásban a this.kilometerAllas egyértelműen a mezőt jelöli, a this nélküli második kilometerAllas pedig továbbra is a paraméterváltozót. A fenti megoldással gyakran lehet találkozni, hiszen egyrészt a programozó mentesül, hogy eltérő névadást kelljen használnia, másrészt a this. kiírása után a "Visual Studio" azonnal felsorolja a mezők (és metódusok) neveit listaszerűen, ezért használata bizonyos szempontból kényelmes is.

THIS felhasználása (2)

Míg az előző felhasználás nem szükségszerű, addig a most ismertetett felhasználás más módon, mint a this használata - nem megoldható:

class Rendorseg
{
  public static KarambolJelentes(TAuto a, TAuto b)
  {
    // az __a__ és __b__ autók karamboloztak, helyszinelés, stb.
    ...
  }
}

class Auto
{
  public void Osszeutkozes(TAuto szembejovo)
  {
    if (sikerulKikerulni(szembejovo)==false)
       Rendorseg.KarambolJelentes(this, szembejovo);
  }
}
// .. főprogram ..
TAuto a= new TAuto("IDE-321","Skoda",75);
TAuto b= new TAuto("BOSS-001","BMW",140);
a.Osszeutkozes(b);
 

A fenti kódban az látszik, hogy amikor az a autónk összeütközés közelébe kerül a b autóval, és nem sikerül elkerülni a balesetet, akkor a rendőrség felé azonnal jelenti a karambolt. Ehhez azonban mindkét, a karambolban részt vevő autót azonosítani kell. Az egyik autó az rendben van, azt megkaptuk paraméterként, ő a szembejovo, a másik autó viszont saját magunk vagyunk. Saját magunkra pedig a this segítségével hivatkozhatunk.

A fenti eset elég gyakori: a metódus belsejéből aktiválunk egy másik metódust, akinek paraméterként át kell adni saját magunkat. Ekkor a this használata nem elkerülhető!

A THIS további jelentései

Megjegyeznénk, hogy a fenti esetek a this kulcsszó metódusok belsejében történő felhasználási esetére vonatkozik.

De a this kulcsszót használjuk akkor is, amikor egyik példányszintű konstruktorból egy másik saját osztálybeli példányszintű konstruktort hívunk:

class Teglalap
 {
     public int X;
     public int Y;
     public int Szeles;
     public int Magas;
     
     public Teglalap():this(0,0,10,10) { }
     
     public Teglalap(int aX, int aY, int aSzeles, int aMagas)
     {
         X = aX;
         Y = aY;
         Szeles = aSzeles;
         Magas = aMagas;
     }
 }
 

Illetve this-nek kell elneveznünk az indexelő propertyt is:

class String
{
 
 public char this[int index]
 {
   get
   {
      // az __index__-edik karakter visszaadása
      return .... ;
   }
 }
}
 
Hernyák Zoltán
2013-03-17 19:03:01