C# Generic Yapılar

Herhangi bir data tip ile çalışabilen type-safe (tip güvenlikli), yeniden kullanılabilir kod yapıları oluşturun.

Konu Başlıklarımız

  • C# Generic Nedir?
  • C# Generics ne zaman kullanılır?
  • Avantajları ve Dezavantajları
  • C# Generic Constraint (Kısıtlamaları)
  • Generic Yapılarda Covariance & Contravariance
  • Generic Koleksiyonlar C#
  • Generic Delegates C#
  • Generic Interfaces C#

C# Generic Nedir?

C# Generic yapılar, C# programlama dilinin yeniden kullanılabilir, type-safe (tip güvenlikli) kodunun oluşturulmasına veya oluşturulmasına izin veren bir özelliğidir. Generic yapıller, belirli bir tipe bağlı olmak yerine herhangi bir veri tipiyle çalışabilen sınıfların, interfaceslerin ve metotların oluşturulmasına izin verir. Buda daha esnek ve yeniden kullanılabilir kod oluşturmayı mümkün kılar.

//Generic Metot Tanımlama
static void PrintData<T>(T data)
{
    Console.WriteLine("Data: " + data);
}

//Generic metotu String data tipinde kullanma

PrintData<string>("Hello World");

//Generi metotu int data tipinde kullanma

PrintData<int>(110);

C# Generic Ne Zaman Kullanılır?

Generic yapılar, farklı türdeki verilerle çalışabilen kod oluşturmanız gerektiğinde kullanışlıdır. Koleksiyonlar, algoritmalar gibi yeniden kullanılabilir bileşenler oluşturmak için kullanılabilirler. Ve özellikle özelleştirilebilen veri yapılarında farklı tiplerle çalışmak için.

Örneğin:
• Herhangi bir tipte nesne oluşturabilen bir koleksiyon sınıfı.
• String, Int veya diğer tipler üzerinde çalışan bir algoritma.
• Herhangi bir veri tipinde depolama yapmak için kullanılan veri yapısı.

C# Generic Constraints (Kısıtlamalar)

C# Generic Constraint (kısıtlamalar), generic yapılarda tipleri sınırlamanın veya kullanılabilecek metot türlerini kısıtlamanın bir yoludur. Örneğin, generic bir tipin veya metodun sadece referans tipleri kabul etmesini sağlamak istiyorsak “class” anahtar kelimesini constraint olarak ekleriz.

public class Print<T> where T : class
{
    //Diğer kodlar...
}

Örneğin yukarıda ki kullanımda T nin yalnızca referans tipli bir değer alabileceğini garanti etmiş oluruz. Diğer kısıtlamalardan “struct” ise tip değerinin yalnızca value type olabileceğini garanti eder, “new()” ise public parametresiz bir constructor (yapıcı) sahip olması gerektiğini garanti eder. Ve son olarak “base sınıf ismi” tipin belirli bir temel sınıftan türetilmesini gerektiğini garanti eder.

Generic Yapılarda Covariance ve Contravariance

C# Generics’teki Covariance ve Contravariance, bir temel sınıfın beklendiği yerde türetilmiş bir sınıfı kullanma becerisini (Covariance) veya türetilmiş bir sınıfın beklendiği yerde bir temel sınıfı kullanma becerisini (Contravariance) ifade eder. Bu kavramlar, dönüş tiplerinde veya parametre tiplerinde kullanılan tür parametrelerine sahip Generic tipler ve metotlarla çalışırken önemlidir.

Aşağıdaki kodda, “Animal” türünde bir instance (örnek) döndüren “GetAnimal” metoduna sahip “IAnimalContainer” Generic bir Interface sahibiz:

public interface IAnimalContainer<out T>
{
    T GetAnimal();
}

C# Generic Yapı Avantajları ve Dezavantajları

C# Generic Yapı Avantajları

  • Tip Güvenliği: Generic yapılar, runtime casting (çalışma zamanı) yerine compile-time (derleme zamanı) tipi güvenliği sağlar.
  • Tekrar Kullanılabilirlik: Generic sınıflar ve metotlar farklı data tipleri için kullanmayı planladığınız yinelenen kodu azaltır.
  • Performans: Generic yapılar, run time (çalışma zamanı) tip denetimini, boxing ve unboxing işlemlerini ortadan kaldırarak performansı artırabilir.

C# Generic Yapı Dezavantajları

  • Complex Code: Generic yapılar kodu anlamayı zorlaştırabilirler.
  • Sınırlı Kısıtlamalar: Generic tipler üzerindeki kısıtlamalar sınırlıdır ve bağımsız değişken tipleri üzerinde kuralların uygulanmasını zorlaştırır.
  • Sınırlı covariance/contravariance: Bazı tipler esnekliği sınırlandırarak covariant veya contravariant bir şekilde kullanılamaz.

C# Generic Collection Kullanma

C# da Generic yapılar collection’larla sıklıkla kullanılır. System.Collections.Generic namespace, belirli bir tipdeki verileri depolamak ve işlemek için generic collection desteği sunar.

Örneğin, List<T> yapısı bir sınıfı veya belirli bir tipdeki öğelerin listesini depolamak için kullanılabilir:

List numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);

C# Generic Delegate Kullanma

Delegate’ler, tip açısından güvenli bir function pointer (fonksiyon/metot göstergesi) tanımlamak için kullanılır. C#’ta, Generic Delegate’ler, herhangi bir tiple çalışabilen, tip açısından güvenli bir function pointer (fonksiyon/metot göstergesi) tanımlamak için kullanılır.

Örneğin, Func<T> delegate, bir Generic T tipinde bir değer döndüren bir işlevi tanımlamak için kullanılabilecek delegate:

Func<int> getInt = () => 14;
int myInt = getInt();
Console.WriteLine(myInt);

Action<T> delegate, T tipinde bir parametre alan ve void döndüren bir işlevi tanımlamak için kullanılabilen bir generic delegate’dir:

Action<int> printInt = (x) => Console.WriteLine(x);
printInt(110);

C# Generic Interface

C# da interface’lerde generic kullanılabilirler. Bu herhangi bir tiple çalışabilen bir interface tanımlayabileceğimiz anlamına gelir.

Örneğin, IComparable interface’i, herhangi bir tiple çalışabilecek bir comparison metot tanımlamak için kullanılabilir:

    public interface IComparable<T>
    {
        int CompareTo(T other);
    }

IEnumerable interface’i, enumerated bir nesne için bir interface tanımlamak için kullanılabilir:

public interface IEnumerable<out T>
{
    IEnumerator<T> GetEnumerator();
}

C# Generic Class ve Iheritance

C#’ta Generic sınıfları Inheritance ile de kullanılabilir. Bu, temel bir generic sınıf oluşturmamıza ve daha özel bir generic sınıf oluşturmak için ondan türetmemize izin verir.

Örneğin, bir generic base class tanımlayalım List<T> ve bu base class’dan türeteceğimiz bir IntList adlı sub class oluşturalım.

public class List<T>
{
    //Class kodlarımız.
}
public class IntList : List<int> 
{ 
    //Class kodlarımız.
}

C# Generic Metotlar ve Overloading

C#’da generic metotlar oluşturabilir ve overloading uygulayabilirsiniz. Bu, herhangi bir tiple çalışabilen metotlar oluşturmamıza ve belirli tipler için özel metotlar oluşturmak üzere bunları overload etmemize olanak tanır.

Örneğin, Print<T> adlı bir generic metot tanımlayalım.

    public void Print<T>(T value)
    {
        Console.WriteLine(value.ToString());
    }

Ayrıca özel bir overload metot ekleyelim.

    public void Print(int value)
    {
        Console.WriteLine("value: " + value.ToString());
    }

C# Generic Boxing ve Unboxing

C# generic yapılarda boxing ve unboxing işlemleri gerçekleştirilir. Boxing, bir value tipini bir nesne tipine dönüştürme işlemidir, Unboxing ise bir nesne tipini tekrar bir value tipine dönüştürme işlemidir.

Örneğin, generic List<int> kullanırken eğer biz bir value tip olan int eklersek otomatik olarak objecte boxing işlemi uygulamış oluruz.

List<int> numbers = new List<int>();
numbers.add(5); //boxing

Değer listeden alınırken, object’den bir int’e geri döndürülmelidir:

int myInt = numbers[0]
//Unboxing

C# Generic Anonymous Metot

C#’da Anonymous metotlar generic yapılarda kullanılabilirler. Bu, bir delege türünü açıkça tanımlamaya gerek kalmadan güvenli bir işlev tanımlamamıza izin verir.

Örneğin, T tipinde bir parametre alan ve int tipinde bir değer döndüren Anonymous bir metot tanımlayabiliriz:

Func<T, int> getCount = delegate (T x)
{
    return count;
};