A tagok olyan “alkatrészei”, egy összetett típusnak (osztály/struktúra), amik a típus egy példányához tartoznak. Ezek lehetnek :
- Field-ek, amik egy objektum belső állapotát tárolják,
- Metódusok, amik műveleteket végrehajtanak az objektummal
- *Property**-k, amik biztonságos és jól szabályozható hozzáférést biztosítanak az objektum belső állapotához.
Lokális és Tagváltozók
Láttuk, hogy létre lehet hozni változókat metóduson belül, Ezen Változókat nevezzük lokális változóknak.
Azt is láttuk, hogy metóduson kívül, de összetett típuson (osztályon vagy struktúrán) belül is létre tudunk, hozni változókat. Ezeket pedig tagváltozóknak, field-eknek, osztály-változónak vagy (legritkábban) mezőnek nevezzük.
using UnityEngine;
class TestClass
{
int someField; // Tagváltozó (Field)
string someOtherField = "Some default value"; // Tagváltozó (Field)
void TestMethod(float someParameter) // A paraméterek is lokális változók
{
int someLocalVariable = 5; // Lokális változó
string someOtherLocalVariable; // Lokális változó
}
}
Egy lokális és egy tagváltozó több mindenben különbözik.
- Lokális változók addig “élnek”, amíg a metódus, amiben létrehozták őket véget nem ér. Ezzel szemben a field addig “elérhető” amíg az osztály.
- Lokális változót a használata előtt kötelező definiálni azaz kezdőértéket adni neki. Ezzel szemben a field-ek kapnak típustól függően egy default (alapértelmezett értéket)
- Lokális változóra csak a definiálása után, de még az adott metóduson belül tudunk hivatkozni.
Összefoglalva
Lokális változó | Osztály változó / Field | |
Hol hozod létre? | Metóduson belül | Osztályon belül |
Életciklus | Addig él, amig a metódus futása tart | Az objektummal egyezik meg |
Definíció
Kezdő érték | Kötelező definiálni, ha ez elmarad, nem használható fel | Ha nem adsz kezdő értéket, automatikusan megkapja típus alapértelmezett értékét |
Elérhetőség | Csak a metóduson belülről | private : Az osztályon belülről
public : Az osztályon belülről és kívülről is |
A this kulcsszó
Lehetséges, hogy egy összetett típusban jelen van egy field meg egy metóduson belüli lokális változó ugyanazon a néven. Ebben az esetben ha a metódusból hivatkozunk a változó nevére, akkor az automatikusan mindig a lokális változót takarja.
TestClass t1 = new TestClass();
t1.Add(5);
Console.WriteLine(t1.num); // 0
class TestClass
{
public int num = 0;
public void Add(int n)
{
int num = 0;
num += n;
// Ez az num egy lokális változó
}
}
TestClass t1 = new TestClass();
t1.Add(5);
Console.WriteLine(t1.num); // 5
class TestClass
{
public int num = 0;
public void Add(int n)
{
// int num = 0;
num += n;
// Ez az num most egy field
}
}
Fenti példában az aláhúzás jelzi ugyanazt a változót.
Kintről a .
(pont ) operátorral tudtunk hivatkozni egy objektum field-jére. Pl.: t1.num
Hasonlóan hivatkozhatnánk az osztályon belül is az objektum field-jére, ha lenne egy hivatkozásunk az adott objektumra.
Egy tag-metóduson belül a this
kulcsszóval tudunk hivatkozni az adott objektumra, amin a metódus épp végrehajtódik.
TestClass t1 = new TestClass();
t1.Add(5);
Console.WriteLine(t1.num); // 0
class TestClass
{
public int num = 0; // ---------------------> Ez ugyanaz a num,
// |
public void Add(int n) // |
{ // |
int num = 0; // Ez ugyanaz a num, // |
num += n; // mint ez! // |
// V
this.num += 5; // <--------------------- mint ez!
}
}
Ez gyakran hasznos lehet egy konstruktor esetén is:
class Person
{
public string name;
public int age;
public int height;
public Person(string name, int age, int height)
{
this.name = name;
this.age = age;
this.height = height;
}
}
A this kulcsszót más kontextusban is használhatjuk arra, hogy az objektumra hivatkozzunk, amin a metódus végrehajtódik.
Változók életciklusa
Deklaráció: Ahhoz hogy használni tudjuk először is létre kell hozni vagy más néven deklarálni a lokális változót, ekkor adunk neki típust és nevet.
Definíció: Mielőtt használni tudjuk a deklarált lokális változónkat kell neki egy kezdőértéket adni.:
Definíció után a változó értékét a blokkon bármikor kiolvashatjuk és felhasználhatjuk bármire.
Bármilyen blokkban ( { }
kapcsos zárójelek közti részben) is hoztuk létre a változót, ha a blokk véget ér, a változó megszűnik létezni.
Példa a helyi változók hatókörére:
void SomeMethod()
{
string v1 = "Variable 1";
if(someCondition)
{
string v2 = "Variable 2";
while(someOtherCondition)
{
string v3 = "Variable 3";
} // v3 hatáskörének vége
} // v2 hatáskörének vége
} // v1 hatáskörének vége
Blokkokról bővebben: Vezérlési szerkezetek, Control Flow - Kódblokk, változók hatásköre, indentálás
Ha az objektum adat típusú, akkor a változó megszűntével a memória is felszabadul, ahol a változó tárolva lett.
Ha viszont az objektum referencia típusú, akkor a változó megszűntével a memória nem szabadul fel ugyanis nem tudhatjuk, hogy valahol létezik-e olyan változó, ami tárolja még mindig a referenciát az adott objektumra.
A referencia típusú objektumok felszabadításáért egy szemétgyűjtő, azaz Garbage Collector felel. Ez a folyamat C#-ban automatikusan megtörténik bizonyos időközönként, ezért a fejlesztőnek nem kell vele foglalkoznia.
Itt jártuk körbe a referencia és értéktípusok témáját: Referencia- és értéktípusok
Egy field életciklusa
A field-et nem kötelező definiálni csak deklarálni.
Ha nem definiálunk egy field-et a deklarációval egy sorban, vagy a konstruktorban, akkor a C# fordító automatikusan beállítja neki a típusának megfelelő alapértelmezett értéket.
Pár típus default értéke:
int
: 0
float
: 0f
bool
: false
struct
: minden field-je kap default értéket a saját típusa szerint
string
: null
(Ez nem egyenlő az üres string-gel)
class
: null
A field addig nem szabadul fel amig az őt tartalmazó objektum nem szabadul fel.