Site icon Serdar Yılmaz

C# – Generic Kısıtlar

Bir önceki yazımızda(*) Generic sınıfların, metotların ve arayüzlerin nasıl oluşturulduğundan bahsettik. Bu yazımızda ise Generic sınıflara, metotlara ve arayüzlere ne tür kısıtlamalar getirebileceğimizden bahsedeceğiz.

Değer ve Referans Tip Kısıtı

public class ExampleClass<T>
{
    public T example_1 { get; set; }
    public T example_2(T parameter)
    {
        return parameter;
    }
}

ExampleClass Generic sınıfına herhangi bir kısıt uygulanmadığı için T yerine int, double, float gibi değer tipleri gönderebileceği gibi; string, object, array gibi referans tipleri de gönderilebilir.

Değer Tipleri: “int”, “long”, “float”, “double”, “decimal”, “char”, “bool”, “byte”, “short”, “struct”, “enum”
Referans Tipleri: “string”, “object”, “class”, “interface”, “array”, “delegate”, “pointer”

Değer ve referans tipler hakkında daha detaylı bilgi edinmek için Değer ve Referans Parametreleri başlıklı içeriği okuyabilirsiniz.

class Program
{
    static void Main(string[] args)
    {
        ExampleClass<int> obj1 = new ExampleClass<int>();
        obj1.example_1 = 18;
        obj1.example_2(128);

        ExampleClass<string> obj2 = new ExampleClass<string>();
        obj2.example_1 = "Serdar YILMAZ";
        obj2.example_2("C# Doküman");
    }
}

Oluşturduğumuz ExampleClass Generic sınıfına sadece referans tip veya sadece değer tip gönderilmesini isteyebiliriz. Böylesi bir durumda Generic sınıfımıza bir kısıt koymamız gerekmekte.

Sadece referans tip gönderilmesi için;  

public class ExampleClass<T> where T:class
{
    public T example_1 { get; set; }
    public T example_2(T parameter)
    {
        return parameter;
    }
}

ExampleClass Generic sınıfına, “where T:class” kısıtı sayesinde sadece referans tipleri gönderilebilir. Değer tipli bir tür gönderildiği takdirde hata oluşacak ve proje derlenmeyecektir.

ExampleClass<string> obj1 = new ExampleClass<string>(); // Geçerli tip
ExampleClass<object> obj2 = new ExampleClass<object>(); // Geçerli tip

ExampleClass<int> obj3 = new ExampleClass<int>();       // HATA! Geçersiz tip
ExampleClass<bool> obj4 = new ExampleClass<bool>();     // HATA! Geçersiz tip

Sadece değer tip gönderilmesi için; 

public class ExampleClass<T> where T : struct
{
    public T example_1 { get; set; }
    public T example_2(T parameter)
    {
        return parameter;
    }
}

ExampleClass Generic sınıfına, “where T : struct” kısıtı sayesinde sadece değer tipleri gönderilebilir. Referans tipli bir tür gönderildiği takdirde hata oluşacak ve proje derlenmeyecektir.

ExampleClass<int> obj1 = new ExampleClass<int>();        // Geçerli tip
ExampleClass<bool> obj2 = new ExampleClass<bool>();      // Geçerli tip

ExampleClass<string> obj3 = new ExampleClass<string>();  // HATA! Geçersiz tip
ExampleClass<object> obj4 = new ExampleClass<object>();  // HATA! Geçersiz tip

new() Kısıtı

Generic sınıfa gönderilen veri tipini, sınıf içerisinde new’liyorsak, yani o tipten yeni bir nesne oluşturuyorsak; Generic sınıfa “new() kısıtını uygulamamız gerekmektedir. Eğer Visual Studio ile geliştirme yapıyorsanız zaten IDE sizi new() kısıtını uygulamaya zorlayacaktır.

public class ExampleClass<T> where T : new()
{
    public T createObject()
    {
        T obj = new T();

        return obj;
    }
}

ExampleClass Generic sınıfında gönderilen veri tipi createObject metodunda new’lenerek yeni bir nesne oluşturulmak istendiğinden new() kısıtı uygulanmıştır. Artık ExampleClass Generic sınıfına sadece nesne oluşturulabilen yani new() lenebilen türler gönderilebilir.

Arayüz kısıtı

Generic sınıfa sadece belirttiğimiz arayüzleri implement alan türlerin gönderilmesini istiyorsak arayüz kısıtını uygularız.

public interface IExample1 { }
public interface IExample2 { }

public class ExampleClass1 : IExample1
{
    public string example_1 { get; set; }
}
public class ExampleClass2 : IExample1
{
    public string example_1 { get; set; }
}
public class ExampleClass3 : IExample2
{
    public string example_1 { get; set; }
}

IExample1 ve IExample2 isminde iki arayüz oluşturduk. IExample1 arayüzünü ExampleClass1 ve ExampleClass2‘ye implement ettik. IExample2 arayüzünü de ExampleClass3 sınıfına implement ettik.

public class GenericClass<T> where T : IExample1
{
    public T example_1 { get; set; }
    public T example_2(T parameter) { return parameter; }
}

Generic sınıfımıza “where T : IExample1” kısıtını eklediğimiz takdirde; Generic sınıfımıza veri türü olarak sadece IExample1 arayüzünü implement alan ExampleClass1 ve ExampleClass2 gönderilebilecektir. ExampleClass3 sınıfı IExample1 arayüzünü implement almadığı için GenericClass’a veri tipi olarak gönderilmek istendiğinde hata alınacaktır. Arayüz kısıtı sayesinde Generic sınıflara sadece belli arayüzleri implement alan sınıfların gönderilmesini sağlayabiliriz.

Birden Fazla Kısıt Ekleme

Yukarıda anlatmış olduğumuz kısıtları birlikte de kullanabiliriz.

public interface IExample1 { }

public class ExampleClass<T> where T : class, IExample1 , new()
{
    public T createObject()
    {
        T obj = new T();

        return obj;
    }
}

ExampleClass Generic sınıfına sadece referans tipli olan, IExample1 arayüzünü implement alan ve new’lenebilen veri türleri gönderilebilir.

new() kısıtı her zaman en son da yer almalıdır.

Generic Metotlara Kısıtların Eklenmesi

Generic kısıtları tıpkı sınıflarda kullandığımız gibi generic metotlarda da kullanabiliriz.

public class ExampleClass
{
    public T createObject<T>() where T : class, IExample1, new()
    {
        T obj = new T();

        return obj;
    }
}

createObject Generic metoduna sadece referans tipli olan, IExample1 arayüzünü implement alan ve new’lenebilen veri türleri gönderilebilir.

Serdar YILMAZ

Exit mobile version