Şablon Türler (Generics)
CLR 1.0 da çalışma zamanında belli olmayan sınıfları kullanan esnek sınıf ve metotlar, Object sınıfı temel alınarak oluşturulması gerekmekteydi. CLR 2.0 dan itibaren Generic lerin kullanımı ile artık böyle bir zorunluluk mevcut değildir. Generic sınıflar olabileceği gibi, generic temsilci, interface ve metotlar da olabilir.
Avantaj / Dezavantaj Performans (Performance) Tip Günvenliği (Type Safety) Tekrar kod kullanımı (Binary code reuse) Kod Fazlalığı (Code Bloat) İsimlendirme (Naming Guidelines)
i)performans Generic lerin en büyük avantajlarından birisi performanstır. Herhangi bir değer tipinden üyeleri olan bir koleksiyon üzerinde işlem yapılırken sık sık boxing/unboxing işlemleri yapılır. Bu ise performansta büyük ölçüde düşüşe neden olur.
Örnek class Program static void Main(string[] args) ArrayList list = new ArrayList(); list.add(44); //boxing int i1 = (int)list[0]; // unboxing foreach (int i2 in list) Console.WriteLine(i2); //unboxing
class Program static void Main(string[] args) List<int> list = new List<int>(); list.add(44); //no boxing - Değer tipleri List<int> içinde saklanmakta int i1 = (int)list[0]; // unboxing, cast işlemine gerek yok. foreach (int i2 in list) Console.WriteLine(i2); //unboxing
ii)tip Güvenliği (Type Safety) class Program static void Main(string[] args) ArrayList list = new ArrayList(); list.add(44); list.add("mystring"); list.add(new Program()); int i1 = (int)list[0]; foreach (int i2 in list) Console.WriteLine(i2);?
class Program static void Main(string[] args) List<int> list = new List<int>(); list.add(44); list.add("mystring"); list.add(new Program()); int i1 = (int)list[0]; foreach (int i2 in list) Console.WriteLine(i2);?
iii) Tekrar Kod Kullanımı (Binary Code Reuse) Generic ikili kodun tekrar kullanımına izin verirler. Generic sınıf bir defa tanımlanır ve başka tiplerde pekçok örneği (instance) oluşturulur. C++ dan farklı olarak kaynak koda erişmek gerekli değildir.
Örneğin List<T> sınıfının (System.Collections.Generic) çeşitli örnekleri aşağıdadır: List<int> list = new List<int>(); list.add(44); List<string> stringlist = new List<string>(); stringlist.add( bilgisayar mühendisliği ); List<MyClass> myclasslist = new List<MyClass>(); myclasslist.add(new MyClass());
iv) Kod Fazlalığı (Code Bloat) Generic sınıf tanımlaması assembly seviyesinde olduğundan, istenilen türde generic sınıf instance ı oluşturmak, IL kodunda bu sınıfların aynılarının üretilmesini sağlamayacaktır. Fakat, generic sınıflar JIT tarafından derlendiğinde, istenilen her bir tür için yeni bir sınıf oluşturulacaktır.
v) İsimlendirme (Naming Guidelines) Generic türler program içerisinde kullanılacağında generic olmayan türlerden ayırt edilebilmesi gerekmektedir. Bunun için: Generic tip isimleri T harfi ile başlamalıdır. Generic türle ilgili eğer özel bir gereksinim veya iki veya daha fazla generic tür kullanılıyorsa tip isimleri için tanımlayıcı isimler seçilmelidir.
Public class List<T> Public class LinkedList<T> Public class SortedList <TKey, TValue>
Generic Sınıfları Oluşturmak (LinkedList Example-Şablon Tipleri Kullanmadan) public class LinkListNode private object value; public LinkListNode(object value) this.value = value; public object Value get return value; set this.value = Value; public LinkListNode Next get; set;
public class LinkListe public LinkListNode Head get; private set; public LinkListNode Tail get; private set; public LinkListNode temp get; private set; public void AddFirst(LinkListNode value) temp = Head; Head = value; Head.Next = temp; public void ekranayaz() temp = Head; while ((temp)!= null) Console.WriteLine("0", temp.value); temp = temp.next;
Generic Sınıfları Oluşturmak (LinkedList Example-Şablon Tipleri Kullanarak) public class LinkListNode<T> private T value; public LinkListNode(T value) this.value = value; public T Value get return value; set this.value = Value; public LinkListNode<T> Next get; set;
public class LinkListe<T> public LinkListNode<T> Head get; private set; public LinkListNode<T> Tail get; private set; public LinkListNode<T> temp get; private set; public void AddFirst(LinkListNode<T> value) temp = Head; Head = value; Head.Next = temp; public void ekranayaz() temp = Head; while ((temp)!= null) Console.WriteLine("0", temp.value); temp = temp.next;
class Singly_Link static void Main(string[] args) LinkListe<string> liste = new LinkListe<string> (); LinkListNode<string> a1 = new LinkListNode<string>("Merhaba"); LinkListNode<string> a2 = new LinkListNode<string>("Bilgisayar"); LinkListNode<string> a3 = new LinkListNode<string>("Mühendisliği"); liste.addfirst(a1); liste.addfirst(a2); liste.addfirst(a3); Console.WriteLine("*************"); liste.ekranayaz();
İkinci örnekte, LinkListe sınıfı, LinkListe<T> yazımı ile şablon türe dönüştürülmüştür. Artık LinkListe<T>, LinkListNode<T> türünden elemanlar alabilir.
Şablon Tülerin Özellikleri (Generic Classes Features)
Örnek public class DocumentManager<T> private readonly Queue<T> documentqueue = new Queue<T>(); public void AddDocument(T doc) lock (this) documentqueue.enqueue(doc); public bool IsDocumentAvailable get return documentqueue.count > 0;
Lock Anahtar Sözcüğü Lock anahtar sözcüğü bir threadin diğer başka bir threadin işlem yaptığı kritik bölgeye girmesini engellemektedir. Lock anahatar sözcüğü ile kilitlenmiş olan nesneneye diğer threadin erişebilmesi için bu nesnenin release edilmesi gerekmektedir. http://msdn.microsoft.com/enus/library/c5kehkcz.aspx
Varsayılan Değerler (Default Values) Şablon bir türe null değerini atamak mümkün değildir. Böyle bir durumda default anahtar sözcüğü kullanılır. Şimdi DocumentManager<T> sınıfımıza GetDocument() metotdunu ekleyelim.
public T GetDocument() T doc = default(t); lock (this) doc = documentqueue.dequeue(); return doc;
Kısıtlar (Constraints) DocumentManager<T> sınıfına DisplayAll Documents() metotdunu ekleyelim.
public void DisplayAllDocuments() foreach (T doc in documentqueue) Console.WriteLine(doc.Title);?
Çalışma zamanında meydana gelen hatanın düzeltilebilmesi için şablon türde olan DocumentManager<T> sınıfında bazı kısıtlamalara gitmek gerekir. Kısıtlamaları sağlamak için where anahtar sözcüğü kullanılır.
public class DocumentManager<T> where T:IDocument private readonly Queue<T> documentqueue = new Queue<T>(); public void AddDocument(T doc) lock (this) documentqueue.enqueue(doc); public bool IsDocumentAvailable get return documentqueue.count > 0;
public T GetDocument() T doc = default(t); lock (this) doc = documentqueue.dequeue(); return doc; public void DisplayAllDocuments() foreach (T doc in documentqueue) Console.WriteLine(doc.Title);
Değişik Kısıtlar da Mevcuttur
Inheritance Şablon bir tür, başka bir sınıftan veya şablon sınıftan türeyebilir. Bir arayüzü implement edebilir. Türeyen sınıf şablon türden olabilir veya olmayabilir.
public class LinkedList<T> : IEnumerable<T> public class Base<T>.. public class Derived<T>:Base<string>
public abstract class Calc<T> public abstract T Add ( T x, T y); public abstract T Sub(T x, T y); public class SimpleCalc:Calc<int> public override int Add (int x, int y) return x+y; public override int Sub(int x, int y) return x - y;
Statik Üyeler Şablon türlerin statik üyeleri sınıfın bir örneği tarafından paylaşılırlar.
public class StaticDemo<T> public static int x; static void Main(string[] args) StaticDemo<string>.x = 4; StaticDemo<int>.x = 5; Console.WriteLine(StaticDemo<string>.x);?
Şablon Arayüzler (Generic Interfaces) Sınıflarda olduğu gibi arayüzler ile birlikte de şablon tipler kullanılabilmektedir.
public interface IComparable<T> int CompareTo(T other); public class Person : IComparable<Person> public int CompareTo(Person other) return this.lastname.compareto(other.lastname);........
Şablon Metotlar (Generic Methods) Şablon türde sınıflar tanımlanabildiği gibi şablon türde metotlar da tanımlanabilir.
class Program public static void Swap<T>(ref T x, ref T y) T temp; temp = x; x = y; y = temp; static void Main(string[] args) int i = 4; int j = 5; Swap<int>(ref i, ref j);
Örnek
public class Account private string name; public string Name get return name; private decimal balance; public decimal Balance get return balance; public Account(string name, Decimal balance) this.name = name; this.balance = balance;
Bütün hesaplar (accounts) bir listede tutulsun. class Program static void Main(string[] args) List<Account> accounts = new List<Account>(); accounts.add(new Account("Christian", 1500)); accounts.add(new Account("Sharon", 2200)); accounts.add(new Account("Katie", 1800));
Toplamı hesaplamak için tüm Account nesneleri üzerinde foreach döngüsü ile dolaşılabilir. Foreach yapısı koleksiyon elemanları üzerinde dolaşabilmek IEnumerable arayüzünü kullandığı için toplamı hesaplamak için yazılacak olan metot da, IEnumerable türünde parametre almalıdır.
public static class Algorithm public static decimal AccumulateSimple(IEnumerable<Account> e) decimal sum = 0; foreach (Account a in e) sum += a.balance; return sum;
AccumulateSimple() metodu aşağıdaki şekilde çağırabilir. public static class Algorithm static void Main(string[] args) List<Account> accounts = new List<Account>(); accounts.add(new Account("Christian", 1500)); accounts.add(new Account("Sharon", 2200)); accounts.add(new Account("Katie", 1800)); decimal amount = Algorithm.AccumulateSimple(accounts);
Aynı örnek için farklı bir Accumulate() metodu yazalım. Şablon metotlarda şablon sınıflarda olduğu gibi where anahtar sözcüğü ile sınırlandırmaya (constraint) gidilebilir.
public static class Algorithm public static decimal Accumulate<TAccount>(IEnumerable<TAccount> coll) where TAccount : IAccount decimal sum = 0; foreach (TAccount a in coll) sum += a.balance; return sum;
class Program static void Main(string[] args) List<Account> accounts = new List<Account>(); accounts.add(new Account("Christian", 1500)); accounts.add(new Account("Sharon", 2200)); accounts.add(new Account("Katie", 1800)); decimal amount = Algorithm.Accumulate<Account>(accounts); Console.WriteLine(amount.ToString());?
public class Account:IAccount private string name; public string Name get return name; private decimal balance; public decimal Balance get return balance; public Account(string name, Decimal balance) this.name = name; this.balance = balance;
Şablon türün parametresi derleyici tarafından, metodun parametresinden otomatik olarak çıkarıldığından aşağıdaki gibi bir yazım da geçerlidir.
class Program static void Main(string[] args) List<Account> accounts = new List<Account>(); accounts.add(new Account("Christian", 1500)); accounts.add(new Account("Sharon", 2200)); accounts.add(new Account("Katie", 1800)); decimal amount = Algorithm.Accumulate(accounts); Console.WriteLine(amount.ToString());
Şablon Temsilciler (Generic Delegates)
Account sınıfına ait örnekteki Accumulate() isimli metodu aşağıdaki gibi değiştirelim.
public static class Algorithm public static TSummary Accumulate <TInput, TSummary> (IEnumerable<TInput> coll, Action<TInput,TSummary> action) TSummary sum = default(tsummary); foreach (TInput input in coll) sum = action(input,sum); return sum; Delegate Türünden IEnumerable Türünden
Accumulate() metodunun ikinci parametresi Action temsilcisi türündendir. Action temsilcisini ise aşağıdaki şekilde tanımlamak mümküdür. public delegate TSummary Action<TInput, Tsummary> (TInput t, TSummary u);
Accumulate() metodu aşağıdaki şekilde invoke edilebilir.
class Program static void Main(string[] args) List<Account> accounts = new List<Account>(); accounts.add(new Account("Christian", 1500)); accounts.add(new Account("Sharon", 2200)); accounts.add(new Account("Katie", 1800)); decimal amount = Algorithm.Accumulate<Account, decimal>(accounts, delegate(account a, decimal d) return a.balance + d;); Console.WriteLine(amount.ToString());