Extension Methods Nedir? (C#)

Extension methods, istediğimiz bir tipe istediğimiz methodu giydirmemizi, onu “genişletmemizi” sağlar. Bu durumda tekrar tekrar kod yazmak yerine, tek bir methodla işimizi halledebiliriz. Örneğin int türünde bir değişkeni yazıp nokta koyduğumuzda ToString() adlı bir method görürüz. Kısacası int değerimizi string türüne çevirmemizi sağlar. Bu methodu, extension methodlara örnek olarak gösterebiliriz. Extension Methods genellikle bir işi birden fazla yerde, aynı tür üzerinde yaptığımızda kullanırız. Hatta ve hatta Extension Methods “tekrar kullanılabilir” özelliğine sahip olduğu için oluşturduğumuz bu kod bloklarını bir kütüphane haline getirip, istediğimiz projeye implement edebiliriz. Şimdi vereceğim birkaç örnek ile işimize mutlaka yarayabilecek kodlar üreteceğiz.

Extension Methods Nasıl Oluşturulur?

Örnek 1:

Oyunumuzda bir EnemySpawner olduğunu düşünelim. Sahnemizde boş bir EnemySpawner objesi oluşturalım ve EnemySpawner.cs sınıfımızı bu objeye atayalım. Bu EnemySpawner objemizin görevi, sahnede düşmanlar spawnlamak olsun. Bu spawnlanan objeleri, oluşturduğumuz EnemySpawner objesinin child’ı yapalım.

public class EnemySpawner : MonoBehaviour 
{

[SerializeField] private GameObject goPrefab;

void Start()
{
    var go = Instantiate(goPrefab, transform.position, Quaternion.identity);
    go.transform.SetParent(transform);
}

}

Her şey güzel görünüyor. Gerekli atamaları yaparak istediğimiz şekilde objemizi spawnladık. Şimdi ise oyunumuzdaki engellerimizi spawnladığımız bir Spawner’ımız olsun. İsmine ObstacleSpawner diyelim. Aynı üstte yaptığımız işlemleri tekrar ObstacleSpawner için yapabiliriz. Fakat burada aynı kodu birden fazla kez tekrarlamış oluruz. Bunun yerine extension method yardımıyla bir şöyle bir şablon oluşturabiliriz:

  • Her yerden kolayca ulaşabileceğimiz ExtensionMethods adında bir public static class oluşturalım.
  • Bu bağlamda oluşturacağımız methodu da public static yapalım.
public static class ExtensionMethods
{
    public static void CreateObjectAndSetParent(this Transform trans, GameObject go, Vector3 pos, Quaternion rot)
    {
        GameObject temp = Object.Instantiate(go, pos, rot);
        temp.transform.SetParent(trans);
    }
}

Parametrelere baktığımızda ilk sırada this keyword’ünü görüyoruz. Extension method oluştururken ilk parametremize this yazıp devamında hangi tipe bu methodu atamak istiyorsak onu yazmamız gerekiyor. Örneğin üstteki örnekte Transform tipine atadık. Herhangi bir MonoBehaviour sınıfında transform. yazdığımızda devamında CreateObjectAndSetParent adlı methodu göreceğiz. Bu methodu çağırdığımız yerde; istediğimiz prefab’ı spawnlayıp, pozisyonunu ve rotasyonunu ayarlayıp, methodu çağırdığımız sınıfı prefablerin parenti haline getirmiş olduk. Şimdi bunu deneyelim.

  • Sahnede ObstacleSpawner objesi oluşturalım ve ObstacleSpawner scriptimizi buna atalım.
  • Script’i açalım ve Start fonksiyonunda transform.CreateObjectAndSetParent methodunu çağıralım.
extension method example

Böylece bir başka Spawner oluşturmak istediğimizde, bu oluşturduğumuz extension method ile kısaca istediğimiz işlemi yapabiliriz.

Örnek 2

İhtiyaç durumunda objelerimizin transformunu sıfırladığımız bir case’imiz olsun. Farklı objeleri sıfırlamak istediğimizde tekrar tekrar sıfırlama işlemi yapacağız ve kod tekrarı olacak:

public void ResetTransformation()
{
    transform.position = Vector3.zero;
    transform.rotation = Quaternion.identity;
    transform.localScale = Vector3.one;
}

Örnek bir transform sıfırlama methodu yazdık. Mesela bir araba objesi için bunu kullandık. Fakat buna ek olarak ana karakterimizin de transformunu sıfırlamak istedik. Tekrardan bunu yazarsak çalışma bakımından hiçbir sorun olmaz fakat kod tekrarı yaptığımız için yavaş yavaş bunlar birikir ve kod karmaşıklığına sebep olur. Olabildiğince aynı şeyleri tekrarlamamalıyız. Şimdi bunu extension method’a çevirelim.

public static void ResetTransformation(this Transform trans)
{
    trans.position = Vector3.zero;
    trans.rotation = Quaternion.identity;
    trans.localScale = Vector3.one;
}

Bu bağlamda istediğimiz bir classta, transform yazdığımızda devamında ResetTransformation adlı bir method olduğunu göreceğiz. Bunu kullanarak istediğimiz objenin transformunu sıfırlayabiliriz.

Örnek 3

Karakterimizi Rigidbody ile kontrol edeceğimiz PlayerController adlı bir script oluşturalım ve karakterimize bunu atayalım. Ardından gerekli değişkenleri oluşturup componenti alalım:

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D _rb;

    void Awake()
    {
        _rb = GetComponent<Rigidbody2D>();
    }

Değişkenimizi tanımladık ve component’e ulaştık. Fakat bazen gerekli component’i objemize eklemeyi unutuyoruz ve null reference hatası alabiliyoruz. Unity tarafından buna geliştirilen çözüm RequireComponent kullanmaktır:

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour

Böylece objemize component eklenmiş oldu ve artık rigidbody kullanıldığı sürece silinemez. Bunun bir diğer yollarından biri generics kullanarak extension method oluşturmaktır. Eğer generics ile ilgili soru işaretleriniz varsa buradan generics ile ilgili yazıma göz atabilirsiniz.

public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
{
    if (gameObject.TryGetComponent(out T requestedComponent))
    {
        return requestedComponent;
    }
    return gameObject.AddComponent<T>();
}

GetOrAddComponent methodumuzu oluşturduk. Bu methodumuz; eğer belirttiğimiz component objede varsa, o component’e ulaşır. Yok ise o component’i objemize ekler. İstediğimiz yerde oluşturduğumuz methodu kullanabiliriz.

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D _rb;

    void Awake()
    {
        _rb = gameObject.GetOrAddComponent<Rigidbody2D>();
    }

Gördüğümüz gibi extension methodlar özellikle genel kullanımlarda inanılmaz kullanışlı. Özellikle sürekli yaptığımız işlemleri extension method haline getirip daha temiz kod yazabiliriz. Bunları biriktirerek güzel bir extension method kütüphanesi oluşturup işlerimizi hızlandırabiliriz. 🙂