A következők megértéséhez elengedhetetlen a statikus- és tagmetódusok megérése:
Változók, tagok és a this kulcsszó Tagváltozók és -metódusok láthatósága Statikus tagok és osztályok
Bármiféle típusú objektumon végzett műveletet megírhatunk statikus metódus formájában. Ezekre a korábban már megismert metódusok közül példák lehetnek:
float randomNum = Random.Range(min, max);
float clampedValue = Mathf.Clamp(value, min, max);
bool isHit = Physics.Raycast(ray, out RaycastHit hitInfo);
float distance = Vector3.Distance(a, b);
Gyakran azonban mindezt más megközelítéssel készítjük el. Egy osztály programozásánál gyakran egy nem statikus metódust (más néven tagmetódust), írunk ekkor az objektumot nem paraméterként adjuk, át hanem “rajta” hívjuk a metódust a .
(pont) operátorral.
someGameObject.SetActive(true);
soemRigidbody.AddForce(force);
Tansform child_10 = someTransform.GetChild(10);
Vector3 normalizedVector = someVector.normalized;
Ezek a metódusok, megírhatók lettek volna statikusként is, ebben az esetben a .
(pont) operátor előtti rész is paramétere lenne a metódusnak.
Miért vannak akkor mégis statikus és nem statikus függvények?
- Ha logikailag úgy gondoljuk, hogy egy műveletet inkább “valamin hajtunk végre” akkor intuitívabb lehet a NEM statikus függvény használata. Pl.:
someVector.Normalize();
normalizálom a vektort, rajta végzek műveletet. - Öröklésnél a tagmetódusokat felül lehet definiálni. Látsd később…
Ezzel szemben, ha a paraméterek logikailag egyenrangúak, akkor célszerűbb a statikus metódust választani. Pl.: float distance = Vector3.Distance(a, b);
két pont távolságát akarom megkapni. Nem igazán lenne logikus, ha egyik lenne a függvény tárgya másik pedig a paramétere.
Ha modern IDE-t (fejlesztőkörnyezetet) használunk, akkor még egy nagy előnye van a tagmetódusoknak a statikus társaikkal szemben:
Ha van egy objektumunk egy változóban (vagy bármi egyéb kifejezésben) és annak leírása után ütünk egy .
-ot (pontot), akkor egyből látjuk az összes elérhető rajta végezhető műveletet és nem kell fejből tudnunk, hogy melyik osztályban találjuk. Ez nagyon kényelmessé teszi a programozást. Megoldható-e hogy statikus metódusaink is rendelkezzenek ezzel az előnnyel?
A kiegészítő metódus: Extensoin Method
A C# kiegészítő metódusok (extension methods) lehetővé teszik a meglévő típusokhoz tartozó új metódusok hozzáadását anélkül, hogy módosítanánk az eredeti típus definícióját. A kiegészítő metódusok olyan statikus metódusok, amelyek úgy terjesztik ki egy adott típus funkcionalitását mintha azok a típus tagfüggvényei lennének.
Ekkor használhatjuk a .
(pont) operátort arra hogy előhívjuk ezeket a lehetőségeket egy adott típusú objektumon. Az extension method fő felhasználása, hogy olyan könyvtárak típusait is ki tudjuk egészíteni, amikhez egyébként nem férünk hozzá. Pl.: GameObject
, Transform
, Vector3
…
Mindez azért igazán hasznos, mert gyakran ilyen metódusokat általában statikus segédosztályokba szoktak ömleszteni, amikkel csak az a gond, hogy csak akkor használjuk őket, ha előre tudunk a létezésükről. Ez a valóságban gyakran ahhoz vezet, hogy ugyanarra a célra több megoldást is lefejlesztünk egy projekten belül, mert nem tudjuk, hogy már valaki készített egyet korábban. Más néven tudtunkon kívül elkövetjük a programozás első számú bűncselekményét: a kód-duplikációt.
Ezt a hibát sokkal nehezebb elkövetni, ha a Visual Studio kódkiegészítője mindig feldobja a megfelelő lehetőséget az adott metódushoz.
Extensoin Method írása
A kiegészítő metódus váza:
public static class <Statikus osztály neve>
{
public static VisszatérésiTípus MetódusNeve(this KiterjesztendőTípus instance, paraméterek...)
{
// Metódus törzse
}
}
A kiegészítő metódusokat a kiterjesztendő típus elé kell írni az this
kulcsszóval, hogy a fordító felismerje, hogy egy kiegészítő metódusról van szó.
Kiegészítő metódust csakis statikus osztályon belül írhatunk.
Példák
Kiegészítő metódusok alkalmazása akkor érdemes, ha nem áll módunkban módosítani a meglévő típus definícióját (pl., ha a típus harmadik féltől származik vagy nem elérhető a forráskódja).
Írjunk pár hasznos példát olyan típusokhoz, amikre igaz a fenti feltétel.
public static class MyExtensions
{
// Törli egy Transform minden gyerekét
public static void DestroyAllChildren(this Transform transform)
{
for (int i = transform.childCount - 1; i >= 0; i--)
GameObject.Destroy(transform.GetChild(i).gameObject);
}
// Ez a metódus count darab másolatot hoz létre egy gameobject-ből
// egy megadott távolságon belül
public static void CloneInRange(this GameObject original, int count, float range)
{
Vector3 position = original.transform.position;
for(int i = 0; i < count; i++)
{
Vector3 randomOffset = new Vector3(
Random.Range(-range, range),
Random.Range(-range, range),
Random.Range(-range, range));
GameObject clone = GameObject.Instantiate(
original,
position + randomOffset,
Quaternion.identity);
}
}
// Összekever véletlenszerűen egy int tömböt
public static void Shuffle(this int[] array)
{
int n = array.Length;
for (int i = n - 1; i > 0; i--)
{
int j = Random.Range(0, i + 1);
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
Ezután a fenti függvények használata:
transform.DestroyAllChildren(); // Minden gyerek objektum törlése egy metódussal
gameObject.CloneInRange(5, 10f); // 5 klón létrehozása max 10 egység távoldágban
myArray.Shuffle(); // Egy tömb tartalmának megkeverés
Egyéb hasznos felhasználása egy Extension Method-nak, hogy olyan típusokhoz is definiálhatunk, tagként viselkedő metódust, aminek nem lehetnek valódi tagmetódusai. pl.: Enum: a felsorolt típus, Interface-ek (Hamarosan)
(Unity 2022 LTS-ben már lehet interface-eken belül metódust definiálni)