İçeriğe geç

Generic Hakkında Her Şey

Bu bölümde elimden geldiği kadar C Sharp’taki Generic yapısından bahsedeceğim İyi okumalar…

C# 2.0 ile birlikte Koleksiyon sınıflarının Generic versiyonları sunuldu ve biz ilk olarak konumuza Generic Class’lar ile başlayacağız.

Generic Classlar

Memik Yanık C# 7.0 kitabında çok güzel bir tanımlama yapmış "Tek bir cümle ile tanımlamak gerekirse, tipi sonradan (nesne hazırlanırken) belirlenen alanlar içeren sınıflara generic sınıf denilmektedir." Generic olmayan sınıfların alanlarında saklanacak veya üzerinde işlem yapılacak bilgileri tipi kodlama esnasında belirlenmektedir. İhtiyaç halinde aldıkları parametler ile geriye döndürülen bilgiin tipinin nesne hazırlanırken belirlendiği metotlar hazırlamak mümkündür.
İlk olarak generic olmayan ArrayList sınıfına bakalım ve bununla igili bir kod yazalım…

static void Main(string[] args)
{
    ArrayList liste = new ArrayList();
    liste.Add(13);
    liste.Add("Nisan");
}

Yukarıda Add metodu object kabul etmektedir. Bundan dolayı derleyici arka planda bir boxing işlemi gerçekleştirerek dönüştürme yapacaktır. Bu kullanımın belirli dezavantajları mevcuttur. Add metodunun her kullanımında boxing işlemi yapılacağından dolayı performans kaybı yaşanacaktr ya da programcı string bilgier eklerken dalgınlıkla int tipinde bir bilgi de ekleyebilir. Buda veri güvenliğini tehlikeye atacaktır. Birde şimdi generic bir class olan List’e bakalım.

static void Main(string[] args)
{
    List<string> liste1 = new List<string>();
    List<int> liste2 = new List<int>();

    liste1.Add("Nisan");
    liste2.Add(13);
}

Yukarıda List tipinde nesneler hazırladık. <> gibi yazılan diamond operatörlere sahip tiplere constructed type denilmektedir. Burada List class’ına bir tip belirterek veri güvenliğini sağlamış olduk. Peki bu tarz generic class’ların temeli nasıl? Aşağıdaki koda bakınız.

public class Liste
{
    public T deneme { get; set; }
}

Burada T yerine istediğimiz tip gelecektir. T kullanımı C# dünyasından bir kültürdür. Örnek kod aşağıdadır.

public class Liste<T>
{
    public T deneme { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        Liste<int> l1 = new Liste<int>();
        l1.deneme = 16;
        System.Console.WriteLine(l1.deneme);//16
    }

}

Burada bilmemiz gereken Liste class’ında bulunan T’ler değişken türleri olacaktır. Başka bir deyişle nereye T koyduysak örnekleme işleminde belirtmiş olduğumuz tip oraya yazılacaktır.

Not: Sınıflarda aynı isme sahip metotlar hazırlanabiliyorsa farkl değişken alanlarına sahip aynı isimli Generic Class’larda oluşturulabilmektedir. Aşağıdaki kod gibi…

public class Liste<T>
{
    public T deneme { get; set; }
}
public class Liste<T1,T2>
{
    public T1 deneme { get; set; }
    public T2[] dizi = new T2[2];
}
class Program
{
    static void Main(string[] args)
    {
        Liste<int> l1 = new Liste<int>();
        l1.deneme = 16;
        System.Console.WriteLine(l1.deneme);//16

        Liste<int,string> l2 = new Liste<int, string>();
        l2.deneme = 13;
        l2.dizi[0] = "Nisan";
        l2.dizi[1] = "BinDokuzyüzDoksanYedi";
        System.Console.WriteLine(l2.dizi[0]);//Nisan
    }

}

Generic Metotlar

Generic class’alrı anladığınızı düşünüyorsanız belki generic metodları aşağıdaki gibi düşünmüş olabilirsiniz.

public class Liste<T>
{
    public T[] liste = new T[5];
    public void Ekle(int index, T eleman)
    {
        liste[index] = eleman;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Liste<string> l1 = new Liste<string>();
        l1.Ekle(0, "Nisan");
        l1.Ekle(1, "Mayıs");

    }

}

Burada metot generic bir parametreye sahip olsa bile generic bir metot değildir. Eğer bu şekilde düşündüyseniz generic tanımını tam anlamıyla anlamamış olabilirsiniz. Bu basamakta geliştirdiğimiz sınıfın değil metodun generic olmasını isteyeceğiz. O zaman aşağıdaki koda bakalım.

public class Liste
{
    public static void Toplama<T>(T s1, T s2)
    {
        Console.WriteLine((Convert.ToInt32(s1) + Convert.ToInt32(s2)).ToString());
    }
}
class Program
{
    static void Main(string[] args)
    {
        Liste.Toplama<int>(12, 16);//26

    }
}

Generic Metotlara Sınırlar Koymak

Bazı durumlarda generic metotlara sınırlamalar koymamız gerekebilir. Örnek olarak generic bir metot içerisinde generic olarak yollanan değişkenleri karşılaştırmak isteyebilirsiniz aşağıdaki gibi bir kod derleme zamanında hata verecektir.

public class GenericMetodlar
{
    public static void Ornek<T>(T x1, T x2)
    {
        if(x1 < x2)
        {
            Console.WriteLine("{0} küçüktür {1}", x1, x2);
        }
        else
        {
            Console.WriteLine("{0} küçüktür {1}", x1, x2);
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        GenericMetodlar.Ornek<int>(2, 4);
    }
}

Bunun sebebi derleyici T tipini bilmediği için bu ifadenin int gelme ihtimali ile string gelme ihtimalini düşünerek string ifadelerde büyüktür küçüktür karşılaştıırlması yapılamayacağından dolayı hata fırlatacaktır. İşte burada generic metodumuza bazı kısıtlar koymamız gerekebilir. Bunu where anahtar kelimesi ile gerçekleştireceğiz. Örnek olarak sayısal değerlerle çalışmak istediğimizi bu metoda söylemek isteyebiliriz bunun için IComparable arayüzünü uygulayabiliriz. Bu arayüzü uygulamamızın sebebi aşağıda ki Int32’nin struct’ından anlaşılmaktadır…

namespace System
{
    //
    // Summary:
    //     Represents a 32-bit signed integer.
    [ComVisible(true)]
    public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>

O zaman kodumuş alağıdaki gibi olacaktır…

public class GenericMetodlar
{
    public static void Ornek<T>(T x1, T x2) where T: IComparable
    {
        if(x1.CompareTo(x2) < 0)
        {
            Console.WriteLine("{0} küçüktür {1}", x1, x2);
        }
        else
        {
            Console.WriteLine("{0} büyüktür {1}", x1, x2);
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        GenericMetodlar.Ornek<int>(2, 4);
    }
}

Generic Sınıflarda Miras

Generic sınıflarda miras olayını kullanabilmek için base class’ın önceden hangi tiple kullanılacağı belirtilmelidir. Ne demek istediğimi aşağıdaki koda bakarak anlayabilirsiniz.

public class Miras<T>
{
    public T[] _degisken = new T[3];
}

public class Ornek: Miras<int>
{

}

Yukarıdaki gibi Miras şeklinde yazılan generic classlara open-constructed generic denilmektedir. Miras olsaydı closed-constructed generic şeklinde isimlendirilmektedir.

Eğer hem derived hemde base class’ın generic olmasını istiyorsanız generic tiplerin aynı olması gerekmektedir. Yani demek istediğim aşağıdaki gibidir.

public class Miras<T>
{
    public T[] _degisken = new T[3];
}

public class Ornek<T1>: Miras<T1>
{

}

Bu şekilde kullanım closed-constructed generic’e örnektir. Ancak aşağıdaki gibi bir kullanım hatalı olacaktır.

public class Miras<T>
{
    public T[] _degisken = new T[3];
}

public class Ornek<T1>: Miras<T>
{

}

Bunun sebebi az öncede dediğim gibi derived class ile base class’ın generic tiplerinin aynı olması gerektiğinden dolayıdır. O zaman bir soru aşağıdaki kod herhangi bir hata verir mi?

public class Miras<T, TT>
{
    public T[] _degisken = new T[3];
    public TT[] _degisken2 = new TT[3];
}

public class Ornek<T1, T2, T3> : Miras<T1, T2> where T1: IComparable
{
    public Ornek(T1 a, T1 b, T1 c) 
    {
        _degisken[0] = a;
        _degisken[1] = b;
        _degisken[2] = c;
    }
    public void Yaz()
    {
        Console.WriteLine(_degisken[0]);
        Console.WriteLine(_degisken[1]);
        Console.WriteLine(_degisken[2]);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Ornek<int, string, bool> o1 = new Ornek<int, string, bool>(13,04,1997);
        o1.Yaz();
    }
}

Bu kod çalışacaktır ve çıktısıda:

13
4
1997

olacaktır.

Generic Interfaceler

Generic interfacelerin normal interfacelerden hiçbir farkı yoktur. Bundan dolayı ekstra bir konu anlatımına gerek olduğunu düşünmüyorum. 2 farklı kullanımıda örek vererek kodları aşağıda paylaştım. Open-constructed generic ve cpen-constructed generic uygulamalı hali sıralı bir şekilde aşağıda paylaşılmıştır.

    public interface IArayuz<T>
    {
        T Fonk();
    }

    public class Ornek<T1> : IArayuz<T1>
    {

        public T1 _deneme;
        public Ornek(T1 deneme)
        {
            _deneme = deneme;
        }
        public T1 Fonk()
        {
            Console.WriteLine("Hepsi bukadar");
            return _deneme;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Ornek<int> a1 = new Ornek<int>(10);
            a1.Fonk();
        }
    }
public interface IArayuz<T>
{
    T Fonk();
}

public class Ornek<T1> : IArayuz<int>
{

    public T1 _deneme;
    public Ornek(T1 deneme)
    {
        _deneme = deneme;
    }

    public int Fonk()
    {
        Console.WriteLine("Hepsi bukadar");
        return Convert.ToInt32(_deneme);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Ornek<int> a1 = new Ornek<int>(10);
        a1.Fonk();
    }
}

new Anahtar Kelimesi

Genericlerle uğraşırken where anahtar kelimesi hakkında örnekler yaptık ancak çokça kullanılan birde new() anahtar kelimesi vardır. new anahtar kelimesi başka bir kısıtlama ile birlikte kullanılacaksa en sonda kullanılmalıdır. Eğer aşağıdaki gibi bir kullanım gerçekleştirirsek T yerine kullanılacak sınıfın yapıcı metodunun herhangi bir parametreye sahip olmadığını veya yapıcı metodunun hazırlanmamış olduğunu belirtmiş oluyoruz.

public class Ornek<T1> where T1 : new()
{
    public Ornek()
    {

    }
}
Kategori:C Sharp

İlk Yorumu Siz Yapın

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir