Unity Eğitim
Dersler
Forum Sınav Merkezi Premium 💎
C# Dersleri

C# Enum ve Struct: Veri Yapılarını Etkin Kullanım Rehberi

Paylaşan: Unity Eğitim 23 November 2025 10 dakika okuma 94 görüntülenme

Giriş: Veri Yapılarının Gücü

Unity ile oyun geliştirirken veya genel C# programlamada, verileri düzenlemek ve yönetmek büyük önem taşır. Bu süreçte karşımıza çıkan temel yapı taşlarından ikisi, C# Enum ve Struct'lardır. Her ikisi de belirli senaryolarda kodunuzu daha okunabilir, güvenli ve performanslı hale getirmek için tasarlanmıştır. Bu makalede, Enum ve Struct'ların ne olduğunu, ne zaman ve nasıl kullanılacağını, pratik ipuçlarını, yaygın hataları ve performans etkilerini detaylı bir şekilde inceleyeceğiz.

C# Enum Nedir ve Neden Kullanılır?

Enum (Enumeration - Numaralandırma), bir dizi adlandırılmış sabit değeri temsil eden özel bir değer tipidir. Genellikle birbiriyle ilişkili, sınırlı sayıda seçeneği ifade etmek için kullanılır. Örneğin, bir oyun karakterinin durumları (Idle, Walking, Running) veya bir kapının açılma yönleri (North, South, East, West) gibi durumlar Enum ile temsil edilebilir.

Enum Kullanımının Avantajları

  • Okunabilirlik: Sayılar yerine anlamlı isimler kullanarak kodun anlaşılırlığını artırır. if (durum == 0) yerine if (durum == KarakterDurumu.Idle) yazmak çok daha açıklayıcıdır.
  • Tip Güvenliği: Bir Enum tipindeki değişken yalnızca o Enum'ın tanımlı değerlerinden birini alabilir. Bu, geçersiz değer atamalarını engeller ve derleme zamanında hataları yakalamanıza yardımcı olur.
  • Bakım Kolaylığı: Yeni bir durum eklemek veya mevcut bir durumu değiştirmek, kod tabanında daha az değişiklik gerektirir.

Temel Enum Tanımlama ve Kullanımı

Bir Enum tanımlamak oldukça basittir:

public enum KarakterDurumu
{
    Idle,      // Varsayılan olarak 0
    Walking,   // Varsayılan olarak 1
    Running,   // Varsayılan olarak 2
    Jumping    // Varsayılan olarak 3
}

public class KarakterKontrolu
{
    public KarakterDurumu mevcutDurum;

    void Start()
    {
        mevcutDurum = KarakterDurumu.Idle;
        Debug.Log("Karakterin başlangıç durumu: " + mevcutDurum);

        if (mevcutDurum == KarakterDurumu.Idle)
        {
            Debug.Log("Karakter boşta.");
        }
    }
}

Enum üyelerine isteğe bağlı olarak özel tamsayı değerleri atayabilirsiniz. Ayrıca, varsayılan olarak int olan temel tipi değiştirebilirsiniz (örn: byte, short, long).

public enum HataKodu : byte
{
    Basarili = 0,
    DosyaBulunamadi = 10,
    ErisimEngellendi = 20,
    VeritabanıHatasi = 30
}

Flags Enum'ları (Bitmasking)

Birden fazla seçeneğin aynı anda aktif olabileceği durumlarda [Flags] özniteliği ve bitwise operatörler ile Enum'ları kullanabilirsiniz. Her Enum üyesine 2'nin katları şeklinde değerler atanır.

[System.Flags]
public enum Guclendirmeler
{
    None = 0,         // Hiçbiri
    Hiz = 1 << 0,     // 1
    Kalkan = 1 << 1,  // 2
    Atak = 1 << 2,    // 4
    Gorunmezlik = 1 << 3, // 8
    Hepsi = Hiz | Kalkan | Atak | Gorunmezlik
}

public class Oyuncu
{
    public Guclendirmeler aktifGuc;

    void ActivatePowerUp(Guclendirmeler guc)
    {
        aktifGuc |= guc; // Güçlendirmeyi ekle
        Debug.Log("Aktif güçlendirmeler: " + aktifGuc);
    }

    bool HasPowerUp(Guclendirmeler guc)
    {
        return (aktifGuc & guc) == guc;
    }

    void RemovePowerUp(Guclendirmeler guc)
    {
        aktifGuc &= ~guc; // Güçlendirmeyi kaldır
        Debug.Log("Kalan güçlendirmeler: " + aktifGuc);
    }

    void Start()
    {
        aktifGuc = Guclendirmeler.None;
        ActivatePowerUp(Guclendirmeler.Hiz);
        ActivatePowerUp(Guclendirmeler.Kalkan);
        
        if (HasPowerUp(Guclendirmeler.Hiz))
        {
            Debug.Log("Oyuncunun hız güçlendirmesi var!");
        }
        RemovePowerUp(Guclendirmeler.Kalkan);
    }
}

C# Struct Nedir ve Neden Kullanılır?

Struct (Yapı), bir değer tipidir (value type). Sınıfların (class) aksine, Struct'lar belleğin yığın (stack) bölgesinde depolanır (büyük Struct'lar veya referans tipleri içeren Struct'lar yığında depolansa bile içindeki referanslar yığına işaret eder). Genellikle küçük, basit veri gruplarını bir arada tutmak için kullanılırlar.

Class ve Struct Arasındaki Temel Farklar

  • Tip: Class referans tipidir, Struct değer tipidir.
  • Bellek: Class örnekleri yığın (heap) üzerinde, Struct örnekleri genellikle yığın (stack) üzerinde oluşturulur.
  • Atama: Class atamaları referansı kopyalar, Struct atamaları tüm değeri kopyalar.
  • Miras: Class'lar miras alabilir ve verebilir; Struct'lar miras alamaz (ancak arayüzleri uygulayabilirler).
  • Null Değer: Class örnekleri null olabilir; Struct'lar varsayılan olarak değer içerir ve null olamaz (ancak Nullable<T> ile null yapılabilirler).

Ne Zaman Struct Kullanılır?

Microsoft'un yönergelerine göre, Struct'ları aşağıdaki durumlarda kullanmak iyi bir fikirdir:

  • Mantıksal olarak tek bir değer temsil ediyorsa (örn: Vector3, Color, DateTime).
  • Örnek boyutu küçükse (genellikle 16 bayttan az).
  • Değiştirilemez (immutable) olması amaçlanıyorsa.
  • Çok sayıda örnek oluşturuluyorsa ve performans kritikse (örneğin, Unity'deki `Vector3` ve `Quaternion` Struct'ları).

Temel Struct Tanımlama ve Kullanımı

Struct tanımlamak class tanımlamaya benzerdir:

public struct Pozisyon
{
    public float x;
    public float y;
    public float z;

    // Parametreli kurucu metot (constructor)
    public Pozisyon(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    // Metotlar da içerebilir
    public float Mesafe(Pozisyon digerPozisyon)
    {
        float dx = x - digerPozisyon.x;
        float dy = y - digerPozisyon.y;
        float dz = z - digerPozisyon.z;
        return Mathf.Sqrt(dx * dx + dy * dy + dz * dz);
    }
}

public class OyunNesnesi
{
    public Pozisyon konum;

    void Start()
    {
        konum = new Pozisyon(10.5f, 5.0f, 2.0f);
        Pozisyon hedefKonum = new Pozisyon(12.0f, 6.0f, 3.0f);

        Debug.Log("Mevcut konum: " + konum.x + ", " + konum.y + ", " + konum.z);
        Debug.Log("Hedefe mesafe: " + konum.Mesafe(hedefKonum));

        // Struct ataması değeri kopyalar
        Pozisyon eskiKonum = konum;
        konum.x += 1.0f; // Sadece 'konum' değişir, 'eskiKonum' değişmez
        Debug.Log("Yeni konum: " + konum.x);
        Debug.Log("Eski konum hala: " + eskiKonum.x);
    }
}

C# Enum ve Struct Birlikte Kullanımı

C# Enum ve Struct genellikle birlikte kullanılarak daha anlamlı ve düzenli veri yapıları oluşturulabilir. Örneğin, bir oyun içindeki eşyaların özelliklerini tanımlayan bir Struct içinde Enum kullanmak oldukça yaygındır.

public enum EsyaTipi
{
    Silah,
    Zirh,
    Ikili,
    Anahtar
}

public struct EsyaOzellikleri
{
    public string ad;
    public EsyaTipi tip;
    public int agirlik;
    public int deger;

    public EsyaOzellikleri(string ad, EsyaTipi tip, int agirlik, int deger)
    {
        this.ad = ad;
        this.tip = tip;
        this.agirlik = agirlik;
        this.deger = deger;
    }

    public void EsyaBilgisiGoster()
    {
        Debug.Log($"Ad: {ad}, Tip: {tip}, Ağırlık: {agirlik}, Değer: {deger}");
    }
}

public class Envanter
{
    public EsyaOzellikleri kılıc;
    public EsyaOzellikleri iksir;

    void Start()
    {
        kılıc = new EsyaOzellikleri("Demir Kılıç", EsyaTipi.Silah, 5, 100);
        iksir = new EsyaOzellikleri("Can İksiri", EsyaTipi.Ikili, 1, 25);

        kılıc.EsyaBilgisiGoster();
        iksir.EsyaBilgisiGoster();

        if (kılıc.tip == EsyaTipi.Silah)
        {
            Debug.Log("Kılıç bir silahtır.");
        }
    }
}

Pratik İpuçları

1. Enum'ları `switch` İfadelerinde Kullanın

Enum'lar, switch ifadeleriyle birlikte kullanıldığında kodunuzu çok daha okunabilir ve yönetilebilir hale getirir. Tüm olası durumları ele aldığınızdan emin olmak için derleyici uyarıları alırsınız.

public void KarakterEylemi(KarakterDurumu durum)
{
    switch (durum)
    {
        case KarakterDurumu.Idle:
            Debug.Log("Karakter bekliyor.");
            break;
        case KarakterDurumu.Walking:
            Debug.Log("Karakter yürüyor.");
            break;
        case KarakterDurumu.Running:
            Debug.Log("Karakter koşuyor.");
            break;
        case KarakterDurumu.Jumping:
            Debug.Log("Karakter zıplıyor.");
            break;
        default:
            Debug.Log("Bilinmeyen durum.");
            break;
    }
}

2. Struct'ları Değiştirilemez (Immutable) Yapın

Struct'ların değiştirilemez olması, özellikle çoklu iş parçacıklı (multi-threaded) ortamlarda hataları azaltır ve kodun anlaşılırlığını artırır. Tüm alanları readonly olarak işaretleyerek ve sadece kurucu metot üzerinden değer atayarak bunu sağlayabilirsiniz.

public readonly struct Renk
{
    public readonly byte R;
    public readonly byte G;
    public readonly byte B;

    public Renk(byte r, byte g, byte b)
    {
        R = r;
        G = g;
        B = b;
    }

    public Renk ToGrayscale()
    {
        byte gray = (byte)((R + G + B) / 3);
        return new Renk(gray, gray, gray);
    }
}

3. `IEquatable` Arayüzünü Struct'larınızda Uygulayın

Struct'lar değer tipleri olduğu için, eşitlik kontrolü varsayılan olarak tüm alanların bit bazında karşılaştırılmasıyla yapılır. Ancak, performans veya özel bir eşitlik mantığı gerekiyorsa IEquatable arayüzünü uygulayarak kendi Equals metodunuzu yazabilirsiniz. Bu, özellikle koleksiyonlarda Struct kullanırken faydalıdır.

public struct Nokta : System.IEquatable
{
    public int X; 
    public int Y;

    public Nokta(int x, int y) { X = x; Y = y; }

    public bool Equals(Nokta other)
    {
        return X == other.X && Y == other.Y;
    }

    public override bool Equals(object obj)
    {
        return obj is Nokta other && Equals(other);
    }

    public override int GetHashCode()
    {
        return System.HashCode.Combine(X, Y);
    }
}

Yaygın Hatalar ve Çözümleri

Enum Hataları

  • Varsayılan Değer Unutulması: Enum'lar varsayılan olarak 0'dan başlar. Eğer 0'ın özel bir anlamı yoksa, ilk değeri açıkça atamak veya 0'a özel bir 'None' veya 'Undefined' değeri vermek iyi bir pratiktir.
  • Geçersiz Değer Ataması: Enum bir tamsayıya dönüştürülebilir ve dışarıdan Enum'da tanımlı olmayan bir tamsayı değeri atanabilir. Bu durum, beklenmedik davranışlara yol açabilir. Her zaman Enum değerlerini kullanmaya özen gösterin veya Enum.IsDefined metodunu kullanarak değerin geçerliliğini kontrol edin.

Struct Hataları

  • Büyük Struct'lar: Struct'lar kopyalandığında tüm içeriği kopyalanır. Çok büyük Struct'lar (örneğin 16 bayttan fazla) performans düşüşüne neden olabilir, özellikle fonksiyonlara parametre olarak geçirildiklerinde. Bu tür durumlarda class kullanmayı veya Struct'ı ref veya in anahtar kelimeleriyle geçirmeyi düşünün.
  • Struct İçinde Referans Tipleri: Bir Struct içinde referans tipi (class örneği, dizi vb.) tutmak mümkündür. Ancak bu, Struct'ın yığında depolanma avantajını kısmen ortadan kaldırır, çünkü referansın kendisi yığında olsa da işaret ettiği nesne yığında (heap) yer alacaktır. Bu, referans tiplerinin hafıza yönetimi karmaşıklığını Struct'lara taşır.
  • Varsayılan Kurucu Metot (Default Constructor): C# 9 öncesinde Struct'lar için parametresiz kurucu metot tanımlanamazdı. C# 10 ile bu kısıtlama kalktı, ancak hala Struct alanlarına varsayılan değer atarken dikkatli olunmalıdır.

Performans ve Optimizasyon Notları

C# Enum ve Struct kullanırken performans, özellikle Unity gibi performans kritik ortamlarda göz önünde bulundurulması gereken önemli bir faktördür.

  • Bellek Ayırma: Struct'lar değer tipi olduğu için genellikle yığın (stack) üzerinde ayrılır. Bu, yığın (heap) üzerinde bellek ayıran ve çöp toplayıcı (garbage collector) tarafından yönetilen referans tiplerine (class'lara) göre daha hızlı bellek yönetimi anlamına gelir. Özellikle oyun döngüsünde sıkça oluşturulan küçük veri yapıları için Struct'lar daha az GC (Garbage Collection) yükü oluşturur.
  • Kopyalama Maliyeti: Bir Struct bir fonksiyona parametre olarak geçirildiğinde veya bir değişkene atandığında, tüm içeriği kopyalanır. Küçük Struct'lar için bu maliyet önemsizken, büyük Struct'lar için önemli bir performans darboğazı olabilir. Bu durumda, Struct'ı in (salt okunur referans) veya ref (okuma/yazma referansı) anahtar kelimeleriyle geçirmek kopyalama maliyetini ortadan kaldırır.
  • readonly struct: C# 7.2 ile tanıtılan readonly struct, Struct'ın tüm alanlarının değiştirilemez olduğunu garanti eder. Bu, derleyicinin bazı optimizasyonlar yapmasına olanak tanır ve Struct'ın yanlışlıkla değiştirilmesini engeller. Performans açısından, özellikle Struct'ı metotlara parametre olarak geçirirken kopya yerine referans kullanılmasını sağlayabilir.

Sonuç

C# Enum ve Struct, C# dilinin temel ve güçlü yapı taşlarıdır. Enum'lar kodunuzu daha okunabilir ve tip güvenli hale getirirken, Struct'lar küçük, değer tabanlı veri yapıları için performanslı ve hafıza dostu çözümler sunar. Bu yapıları ne zaman ve nasıl kullanacağınızı anlamak, daha sağlam, verimli ve bakımı kolay Unity oyunları ve C# uygulamaları geliştirmenize yardımcı olacaktır. Doğru yerde doğru yapıyı kullanmak, profesyonel bir geliştiricinin en önemli becerilerinden biridir.

🧠 Ders Sonu Değerlendirme Testi

Dersi tamamladıktan sonra bilgilerinizi test edin ve ekstra puanlar kazanın.

🔥 +50 XP Ödül
🔒

Sınava Katılmak İçin Giriş Yapın

Bu ders sonu testini çözebilmek, bilginizi test edip **+50 XP** kazanmak ve **Sınav Şampiyonu** rozetinin kilidini açmak için üye girişi yapmalısınız.

Yorumlar (0)

Yorum yazabilmek ve derslere katkıda bulunabilmek için giriş yapmalısınız.

İlk yorumu siz yapın!