Çoğu zaman işlem yaptığımız metotlar parametre olarak belli tipleri içerisine alır ve bunlar üzerinden işlem yaparlar. Ancak bu metotları tip bağımsız yapmak mümkün. Böyle birşey söz konusu olduğunda geçmiş zamanlarda benim de aklıma ilk gelen metotların parametrelerini object tipinden tanımlamak olmuştu. Ancak bu işlemin getirdiği bazı sıkıntılar söz konusu. Birincisi metotların parametrelerini object tanımladığınız ve bir veri göndermek istediğinizde ki bu veri değer tipki (int,string,double..) veya referans tipli (class vs) olabilir, Değer tipli olursa değer boxing olarak adlandırdığımız bir casting işlemi söz konusu olacaktır ki bu maliyetli bir işlemdir. Bunun yanısıra, Örneğin iki sayının birbiriyle eşitliğini sorguladığımız bir metot geliştiriyor olalım. Bu metotun parametreleri object tipinde olsun ve metot içerisinde kıyaslansın. Ancak biz bu durumda metoda parametre olarak bir parametresine string diğerine ise int gönderebiliriz ve hata almayız ancak bu gibi durumlarda derleme aşamasında hatayı görmemiz daha doğru olur.
.Net bunun için generic yapılar adında bir yapı geliştirmiştir ve tip bağımsız yapılar oluşturmayı sağlamıştır.
aşağıdaki metot isminin yanına <T> şeklinde yazdığımız ifade T tipinde yeni bir tip tanımladığımızı ve bu tipin metot içerisinde geçerli olacağını belirtir. Parametreler de T tipinde parametreler olarak belirtilmiştir. Ancak jenerik tiplerde işlem uygularken uygun metotlar kullanılmalıdır.
public static bool areEqual<T>(T x, T y) { return x.Equals(y); }
Bu metodu çağırırken ise
Class1.areEqual(1, 2); veya Class1.areEqual(“A”, “B”); formunda yazmamız yeterli olacaktır. Ancak daha metotu çağırıken verilmesi gereken parametreleri garantilemek istersek
Class1.areEqual<int>(1,2); şeklinde bir gönderimi de tercih edebiliriz.
1.Jenerik Classlar
İstenildiği takdirde classları da jenerik olarak tanımlayabiliriz.
class List<T> { T t1; public T Deger{ get{return t1;} set {t1 = value;} } }
yukarıdaki classa ait bir nesne üretirken
List<string> obj = new List<string>(); şeklinde üretiriz bu üretme sonucunda class tanımında yer alan tüm T tipi string ile eşleşmiş olur. List<int> obj2 = new List<int>(); ile de tüm T leri int ile eşlemiş oluruz.
2.İç içe jenerik classlar
Aşağıda da iç içe class yapısı , bu classların jenerik yapısı ve bu classlardan nesne üretimi örneklenmiştir.
class Program { static void Main(string[] args) { disCls<string> o1 = new disCls<string>(); disCls<string>.IcCls<int> o2 = new disCls<string>.IcCls<int>(); Console.ReadKey(); } } class disCls<U> { public disCls() { Console.WriteLine("Dıs calis constructur"); } public class IcCls<V> { public IcCls() { Console.WriteLine("İç sınıf constructor"); } } }
3.Tip kısıtlamaları
Tip tutucu bir tip tanımlarken bazı kısıtlamalar da kullaniliriz.
Örneğin
<T> where T : class ifadesi T tipi sadece referans olan bir class olabilir demektir yani int,string,double gibi ifadeleri kabul etmez.
<T> where T : struct ifadesi T tipi sadece string ifade tutacak anlamındadır.
class Program { static void Main(string[] args) { //Hatalı MyCollection2<int> o = new MyCollection2<int>(); MyCollection1<double> o2 = new MyCollection1<double>(); Console.ReadKey(); } } public class MyCollection1<T> where T : struct { } public class MyCollection2<T> where T : class { }
- tip tutucuları ve kısıtlarını aşağıdaki yapı ile çoğaltabiliriz. Bu sayede class içerisinde birden fazla farklı tip tutucu tanımlayıp bunlara kısıtlar atayabilir. Gerekli yerlerde kullanabiliriz.
public class MyCollection1<T,U,V> where T : struct where U : class { }
4.İnterface şartı
Aşağıdaki yapı örneğiyle de K tip tutucusu IComparable interfacesini implemente etmiş bir tip olmalıdır şartını sunmuş oluruz. Bunu söyleyerek Knin referans tipli olacağını da söylemiş oluruz.
public class Dictionary<K, U> where K : IComparable { }
- Bazen oluşturduğumuz tip tutucuları başlangıçta boş değer içerecek şekilde oluşturmamız gerekebilir ancak burda şöyle bir sıkıntı ortaya çıkar. Referans tipli değişkenlerin boş değeri null iken değer tipli değişkenlerin boş değeri 0 veya o değere uygun değerdir ancak bir değerdir. Bu da bize program yazarken hata yapma ve işi uzatma … Bunun önüne geçebilmek için default(t); metodunu aşağıdaki şekilde kullanırız default metodu yapıyı oluştururken verdiğimiz tipin null değerini tespit edip onu otomatik olarak değişkene atar.
class Program { static void Main(string[] args) { MyGenCls<SqlConnection> o1 = new MyGenCls<SqlConnection>(); if (o1.Obj == null) Console.WriteLine("o1 nesnesi nulldir"); MyGenCls<int> o2 = new MyGenCls<int>(); if (o2.Obj == 0) Console.WriteLine("o2 nesnesi nulldır"); Console.ReadKey(); } } class MyGenCls<T> { public T Obj; public MyGenCls() { Obj = default(T); } }