Generic Kısıtlamaları
C# ta generic-tip parametrelerinin yerine hangi kısıtlamalarda client-tanımlı parametrelerin kullanılacağını derleyiciye belirtmek zorundasınızy. Üç tip kısıtlama vardır. Türeme kısıtlaması derleyiciye generic-tip parametrenin hangi temel tipte class veya interface den türemesi gerektiğini belirtir. Varsayılan yapıcı (default constructor) kısıtlaması derleyiciye generic-tip parametresinin default public contructor a (parametresiz public constructor) sahip olduğunu gösterir. Referans/Değer tip kısıtlayıcısı generic tip parametrenin referans mı yoksa değer tipindemi olduğunu sınırlar. Bir generic tip birden fazla kısıtlayıcı barındırabilir.
Şunu da belirtmek lazım ki bu kısıtlayıcılar opsiyoneldir, ama generic-tip parametreler kullanırken bunları kullanmak mecburi gibidir. Bunları kullanmadığınız sürece object-tip parametrelerde olduğu gibi daha fazla tip-güvenlik kontrolleri yapmanız gerekir.Kısıtlamalar generic-tip metadatanın önemli bir parçasıdır, bu sayede client-side derleyiciside bunun avantajlarından faydalanabilir. Client-side derleyicisi client geliştiricisine kısıtlamalara uymasını zorlayarak tip-güvenliği sağlar.
Kısıtlamaların kullanım ihtiyacına yönelik bir örnek verecek olursak: Bağlı listeye indexleme kabiliyeti ve bir key e göre arama kazandırmak isteyelim
public class LinkedList
{
T Find(K key)
{...}
public T this[K key]
{
get{return Find(key);}
}
}
Bu clienta aşağıdaki kodu yazmasına olanak sağlar.
LinkedList list = new LinkedList();
list.AddHead(123,"AAA");
list.AddHead(456,"BBB");
string item = list[456];
Debug.Assert(item == "BBB");
Arama yapabilmek için tüm listeyi taramak, her bir node un key ini aranan key ile karşılaştırmak ve eşleşen node u geri döndürmek gerekir. Problem şu ki aşağıdaki method derlenmez:
T Find(K key)
{
Node current = m_Head;
while(current.NextNode != null)
{
if(current.Key == key) //Derlenmez...
break;
else
current = current.NextNode;
}
return current.Item;
}
Neden derlenmediğinin sebebi:
if(current.Key == key)
Yukardaki satır derlenmez çünkü derleyici K nin == operatörünü destekleyip desteklemediğini bilemez. Mesela structlar bu çeşit bir gerçekleştirmeyi desteklemez. Bu == operatör kısıtlamasını aşmak için IComparable arayüzünü kullanabilirsiniz:
public interface IComparable
{
int CompareTo(object obj);
}
CompareTo() methodu eşitlik durumunda 0 döndürür, böylece Find() methodu bunu aşağıdaki gibi kullanabilir:
if(current.Key.CompareTo(key) == 0)
Malesef bu kodda derlenmez çünkü derleyici K nın IComparable dan türeyip türemediğini bilme imkanı yoktur.
Siz bunu IComparable a cast edip kullanabilirsiniz ama bu da tip-güvenliği için pahalıya mal olur:
if(((IComparable)(current.Key)).CompareTo(key) == 0)
Türeme Kısıtlaması
C# 2.0 da kısıtlama tanımlamak için where keyword ünü kullanırız. where keyword ünü derleyiciye parametrenin neden türemesi gerektiğini belirtmek için kullanırız.
public class LinkedList where K : IComparable
{
T Find(K key)
{
Node current = m_Head;
while(current.NextNode != null)
{
if(current.Key.CompareTo(key) == 0)
break;
else
current = current.NextNode;
}
return current.Item;
}
//Rest of the implementation
}
Derleyici burada list in key argümanı için IComparable dan türeme şartı koşucak ve kodu eğer key bundan türemiyorsa derlemiyecektir.
Kısıtlama size IComparable ı kullanmanıza izin verse bile, key olarak değer tipinde, mesela integer tipinde bir değişken kullandığınızda tekrar boxing yapmanız gerekecek. Bu sorunu aşmak için IComparable kullanmanız gerekecek:
public interface IComparable
{
int CompareTo(T other);
bool Equals(T other);
}
Key için kullanımı aşağıdaki gibidir.
public class LinkedList where K : IComparable
{...}
Kısıtlamalar gerçek türemelerden sonra yazılmalıdır. Mesela LinkedList IEnumerableinterface inden türüyorsa where bundan hemen sonra yer almalıdır:
public class LinkedList : IEnumerable where K : IComparable
{...}
Aynı generic tip parametresinde birden fazla interface kısıtlaması getirebilirsiniz Mesela:
public class LinkedList where K : IComparable,IConvertible
{...}
Her bir generic tip parametre için kısıtlama yazılabilir, mesela:
public class LinkedList where K : IComparable
where T : ICloneable
{...}
Generic tip parametreler için class dan türeme kısıtlamasıda getirebilirsiniz. Ama sadece bir tane belirleyebilirsiniz:
public class MyBaseClass
{...}
public class LinkedList where K : MyBaseClass
{...}
C# kısıtlama olarak başka bir generic tip parametre kullanmanızada olanak sağlar:
public class MyClass where T : U
{...}
Constructor Kısıtlaması
Generic sınıfın içinde bir generic nesne oluşturmak istediğinizi düşünün. C# derleyicisi clientın kullanacağı tip argümanın tipinde argümanlara sahip bir constructor olup olmadığını bilemez, böylece derleme yapamaz.
Bu problemi aşmak için C# size public default contructor tanımlamanıza olanak tanır. Bu new()kısıtlaması kullanarak yapılır. Örneğin:
class Node where T : new()
{
public K Key;
public T Item;
public Node NextNode;
public Node()
{
Key = default(K);
Item = new T();
NextNode = null;
}
}
Türeme kısıtlaması ile birlikte kullanımı aşağıdaki gibidir:
public class LinkedList where K : IComparable,new()
{...}
Referans/Değer Tip Kısıtlaması
Generic tip parametresinin değer tipindemi yoksa başka tiptemi olacağını aşağıdaki gibi kısıtlayabilirsiniz:
public class MyClass where T : struct
{...}
Aynı şekilde referans tipinde olduğunuda class keywordü ile kısıtlayabilirsiniz:
public class MyClass where T : class
{...}