Developedia
Developedia
Kiegészítő metódusok

Kiegészítő metódusok

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óVáltozók, tagok és a this kulcsszó Tagváltozók és -metódusok láthatóságaTagváltozók és -metódusok láthatósága Statikus tagok és osztályokStatikus 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?

  1. 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.
  2. 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.

  3. Öröklésnél a tagmetódusokat felül lehet definiálni. Látsd később…

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.

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ípusEnum: a felsorolt típus, Interface-ek (Hamarosan)Interface-ek (Hamarosan)

(Unity 2022 LTS-ben már lehet interface-eken belül metódust definiálni)

Logo

Főoldal

Blog

Elmélet

3D Studio

Adatvédelmi nyilatkozat

GY.I.K.

Házirend

Szerző: Marosi Csaba / marosi.csaba@3d-studio.hu

DiscordGitHubLinkedIn
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;
    }
	}
}