C# dilinde generic (jenerik) yapılar, türden bağımsız olarak veri yönetimi yapmayı mümkün kılan güçlü bir özelliktir. Bu yapılar sayesinde kod tekrarını azaltırken, aynı zamanda tür güvenliği ve performansı artırabilirsiniz. Generic yapılar özellikle koleksiyonlar, metotlar ve sınıflar üzerinde esnek ve sağlam çözümler sunar.

Generic Nedir ve Neden Kullanılır?

Generic yapılar, .NET 2.0 ile birlikte C#’a eklenen önemli bir özelliktir. Generic’ler sayesinde, sınıf, arabirim, metot veya koleksiyon gibi yapıların çalışacağı veri türünü belirlemeden genel bir şablon oluşturabiliriz. Gerçek tür ise daha sonra bu şablon üzerinden nesne oluştururken belirlenir.

Örneğin, List<T> gibi bir koleksiyon, farklı türlerde verileri tutmak için kullanılabilir:

List<int> sayilar = new List<int>();
List<string> isimler = new List<string>();

Bu sayede, her bir liste yalnızca belirli bir türde eleman tutar ve boxing/unboxing işlemi gerekmez. Bu da hem performans hem de tür güvenliği açısından büyük avantajlar sağlar.

Generic Sınıflar ve Metotlar

Generic sınıflar ve metotlar, C#’da çok sık kullanılan yapılardır. Kendi generic sınıfınızı veya metotlarınızı tanımlayarak tekrar kullanılabilir ve esnek kodlar yazabilirsiniz.

Generic Sınıf Tanımlama

Aşağıda basit bir generic sınıf örneği verilmiştir:

public class Kutu<T>
{
    private T icerik;

    public void IcerikAta(T deger)
    {
        icerik = deger;
    }

    public T IcerikAl()
    {
        return icerik;
    }
}

Bu sınıf ile farklı türlerde içerik saklayabilirsiniz:

Kutu<int> sayiKutusu = new Kutu<int>();
sayiKutusu.IcerikAta(100);
int sayi = sayiKutusu.IcerikAl();

Kutu<string> metinKutusu = new Kutu<string>();
metinKutusu.IcerikAta("Merhaba");
string metin = metinKutusu.IcerikAl();

Generic Metotlar

Generic metotlar, belirli bir sınıfın içinde veya bağımsız olarak tanımlanabilir. Tür parametresi, metot imzasında belirtilir:

public static T EnBuyuk<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

Bu metot, iki değeri karşılaştırarak büyük olanı döndürür. where T : IComparable<T> ifadesi, T türünün karşılaştırılabilir olması gerektiğini belirtir. Kullanımı şu şekildedir:

int buyukSayi = EnBuyuk(5, 10);
string buyukKelime = EnBuyuk("elma", "armut");

Generic Koleksiyonlar

C#’ta generic koleksiyonlar, verileri tür güvenli bir şekilde saklamak için kullanılır. System.Collections.Generic namespace’i altında yer alırlar. En yaygın kullanılan generic koleksiyonlar şunlardır:

  • List<T>: Dinamik boyutlu dizi benzeri yapı.
  • Dictionary<TKey, TValue>: Anahtar-değer çiftlerini saklar.
  • Queue<T>: FIFO (First In First Out) yapısında veri saklar.
  • Stack<T>: LIFO (Last In First Out) yapısında veri saklar.
  • HashSet<T>: Benzersiz elemanlardan oluşan bir koleksiyondur.

Bu koleksiyonlar, veri türünü önceden belirleyerek hem performans hem de güvenliği artırır. Örneğin:

Dictionary<string, int> ogrenciNotlari = new Dictionary<string, int>();
ogrenciNotlari.Add("Ahmet", 85);
ogrenciNotlari.Add("Mehmet", 92);

foreach (var item in ogrenciNotlari)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

Generic Kısıtlamalar (Constraints)

Generic yapılar kullanılırken, bazı senaryolarda türlerin belirli özelliklere sahip olmasını isteyebiliriz. Bu durumda where anahtar kelimesi ile kısıtlamalar tanımlanabilir.

  • where T : class → T sadece referans tür olabilir.
  • where T : struct → T sadece değer türü olabilir.
  • where T : new() → T’nin parametresiz bir constructor’ı olmalı.
  • where T : <interface> → T, belirtilen arabirimi uygulamalı.
  • where T : <base class> → T, belirtilen sınıftan türemeli.

Kısıtlamalar, generic yapıların daha güvenli ve işlevsel hale gelmesini sağlar. Örneğin:

public class VeriIsleme<T> where T : class, new()
{
    public T YeniNesneOlustur()
    {
        return new T();
    }
}

Bu sınıf, yalnızca parametresiz constructor’a sahip referans türlerini kabul eder.

Generic Arabirimler ve Delegate’ler

Generic yapılar sadece sınıflar ve koleksiyonlarla sınırlı değildir. Generic arabirimler ve delegate’ler de tanımlanabilir. IEnumerable<T>, IComparable<T> ve Func<T, TResult> gibi yapılar bu kapsamda yer alır.

Func<int, int, int> toplama = (x, y) => x + y;
int sonuc = toplama(3, 7); // 10

Bu örnekte, Func<T, TResult> delegate’i generic olarak tanımlanmış ve iki int değeri alıp int döndüren bir fonksiyonu temsil etmektedir.

Generic Kullanmanın Avantajları

  • Tür Güvenliği: Derleme zamanında tür kontrolü yapılır.
  • Performans: Boxing/unboxing işlemi gerekmez.
  • Kod Yeniden Kullanımı: Aynı yapı farklı türlerle kullanılabilir.
  • Okunabilirlik ve Bakım Kolaylığı: Kod daha anlaşılır ve sade olur.

Sonuç

C# dilinde generic yapılar, veri yönetimini esnek ve güvenli hale getiren güçlü bir özelliktir. Generic koleksiyonlar, sınıflar ve metotlar sayesinde daha az kodla daha fazla iş yapmak mümkün olur. Ayrıca, tür güvenliği ve performans konularında da önemli kazanımlar sağlar. Doğru kullanıldığında, kodunuz daha sürdürülebilir ve okunabilir hale gelir.