Ş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;
} } }
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; }
Generic Sınıfları Oluşturmak
(LinkedList Example-Şablon Tipleri Kullanarak)
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/en-
us/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());
} }