Giriş
Generic sınıflar C# 2.0 'ın en güçlü özelliğidir. Generic sınıflar size type-safe veri yapıları tanımlamanızı sağlar. Bu size yüksek performans ve kaliteli kod yazmanızı sağlar. Çünkü yazdığınız kodu başka tip verilere uygun olarak tekrardan yazmadan kullanabilmenizi sağlar. Konsept olarak C++ taki templatelere benzer ama uygulama ve yapabilecekleri açısından oldukça farklıdır. Bu makalede bu yapının faydaları, neden ihtiyaç olduğu ve nasıl kullanılacağı konusuna bir başlangıç yapılacaktır.
Generic Sınıflar Problem Aşaması
Klasik Push() ve Pop() metodları sağlayan bir stack veri yapısını düşünün. Genel amaçlı bir stack veri yapısı geliştirirken, onu değişik tiplerde verileri tutmak için kullanmak istersiniz. C# 1.1 de, Object tabanlı stack kullanmak zorundaydınız.
public class Stack
{
object[] m_Items;
public void Push(object item)
{...}
public object Pop()
{...}
}
Kod Blok 1 Object-tabanlı stack yapısının nasıl gerçekleştirildiğini gösteriyor. çünkü Object .Net in en temel veri tipidir. Ve bu sayede istenilen tipte veriler tutabilirsiniz stack veri yapısında. Örneğin integer bir değer için:
Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
int number = (int)stack.Pop();
Kod Blok 1. Object-Tabanlı bir stack yapısıpublic class Stack
{
readonly int m_Size;
int m_StackPointer = 0;
object[] m_Items;
public Stack():this(100)
{}
public Stack(int size)
{
m_Size = size;
m_Items = new object[m_Size];
}
public void Push(object item)
{
if(m_StackPointer >= m_Size)
throw new StackOverflowException();
m_Items[m_StackPointer] = item;
m_StackPointer++;
}
public object Pop()
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
throw new InvalidOperationException("Boş stacktan pop yapamazsınız");
}
}
}Ama Object-Tabanlı çözümde 2 problem karşımıza çıkmaktadır. İlk olarak performans sorunu. Dğer tipindeki veri tiplerini kullanırken, önce onu boxing ile object ye çevirip (boxing) stack a atmak daha sonrada onu istediğiniz değer tipine çevirmeniz (unboxing) gerekir. Boxing ve unboxing kendi başına bir performans sorunudur aynı zamanda daha fazla garbage collection manasınada gelir.Referans tipi veriler kullanırkende büyük sıkıntı yaratır. Çünkü bu tipleride Object tipinden asıl tipe çevirmeniz gerekmektedir:
Stack stack = new Stack();
stack.Push("1");
string number = (string)stack.Pop();İkinci (ve daha önemli olanı) is tip güvenliğidir. Çünkü derleyici size Object tipinden herhangi bir değeri cast yapabilmenize izin verir. Böylece derlenme zamanı tip güvenliğini kaybedersiniz. Örneğin aşağıdaki kod derlenme sırasında sorun çıkartmaz ama çalışma sırasında hata alırsınız:
Stack stack = new Stack();
stack.Push(1);
//Aşağısı derleme sırasında sorun çıkarmaz ama çalışırken hata alırsınız.
string number = (string)stack.Pop();
Bu problemleri tip-özel stack yaparak aşabilirsiniz. Mesela integer değerler için IntStack kullanabilirsiniz:
public class IntStack
{
int[] m_Items;
public void Push(int item){...}
public int Pop(){...}
}
IntStack stack = new IntStack();
stack.Push(1);
int number = stack.Pop();
String değerler için StringStack:
public class StringStack
{
string[] m_Items;
public void Push(string item){...}
public string Pop(){...}
}
StringStack stack = new StringStack();
stack.Push("1");
string number = stack.Pop();
Ama bu şekilde performans ve tip-güvenlik problemlerini çözmek bizi üçüncü bir probleme götürür — verimlilik. Tip-Özel veri yapıları yazmak can sıkıcı, aynı şeylerin tekrardan yazılması ve hata eğilimli bir yapıdır. Bir veri yapısındaki hatayı düzeltirken, diğer tipler için tanımlı yapılarıda düzeltmeniz gerekir. Sonuç olarak C# 1.1 geliştiricileri Object-tabanlı veri yapılarının pratik olmadığı ve kullanım özelliğinin kalmadığı sonucuna vardılar.
Hiç yorum yok:
Yorum Gönder