Generics etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Generics etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

27 Temmuz 2011 Çarşamba

C# Generic Sınıflara Giriş #2

Generics Nedir

Generics size tip-güvenliği ve performansı düşünmeden generic sınıflar yapabilmenizi sağlar.Sunucuyu yanlızca bir kere generic sunucu olarak implement edersiniz, aynı zamanda istediğiniz tip için kullanabilrsiniz. Bunu yapabilmek için < ve > parantezlerinin içine kullanacağımız tipi yazarız. Mesela aşağıda bir generic stack yapısının nasıl olduğu  görülmektedir:


public class Stack
{
   T[] m_Items; 
   public void Push(T item)
   {...}
   public T Pop()
   {...}
}
Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
int number = stack.Pop();
Kod Blok 2 generic stack yapısını göstermektedir.  Kod Blok 1 ile Kod Blok 2 yi karşılaştırın göreceksiniz ki, Stack tanımı haricindeki her yerde  Code block 1 deki object yerine Kod Blok 2, ye T yerleştirilmiştir:
public class Stack
{...}
Generic stack yapısını kullanırken compiler ı generic parametre olan T yerine hangi tipin kullanılacağını hem stack yapısını tanımlarken hemde başlatırken bilgilendirmemiz gerekiyor:

Stack stack = new Stack();
Compiler ve runtime geri kalan herşeyi yapar.
Kod Blok 2. Generic Stack Yapısı
public class Stack
{
   readonly int m_Size; 
   int m_StackPointer = 0;
   T[] m_Items;
   public Stack():this(100)
   {}
   public Stack(int size)
   {
      m_Size = size;
      m_Items = new T[m_Size];
   }
   public void Push(T item)
   {
      if(m_StackPointer >= m_Size) 
         throw new StackOverflowException();
      m_Items[m_StackPointer] = item;
      m_StackPointer++;
   }
   public T Pop()
   {
      m_StackPointer--;
      if(m_StackPointer >= 0)
      {
         return m_Items[m_StackPointer];
      }
      else
      {
         m_StackPointer = 0;
         throw new InvalidOperationException("Cannot pop an empty stack");
      }
   }
}

24 Mayıs 2011 Salı

Generic Kullanımı #2

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 
{...}

21 Mayıs 2011 Cumartesi

Generic Kullanımı #1

Generic Kullanımı


Generic tipleri sınıflar ve struct lar için kullanabilirsiniz. İşte size faydalı bir generic point (nokta) yapısı:

public struct Point
{
   
   public T X;
   
   public T Y;
}


Generic nokta (point) yapısını integer koordinatlar için kullanabilirsiniz. Örneğin:

Point point;
point.X = 1;
point.Y = 2;


Veya double tipinde koordinatlar için:

Point point;
point.X = 1.2;
point.Y = 3.4;


Şimdiye kadar generic tiplerin temel kullanımını gördük. C# 2.0 ın bazı generice özel syntax yapısıda vardır. Mesela  default() operatörü kullanılan tipin default değerini döndürür. Örneğin stack yapısında listeden pop yapmak istediğimizde kullandımız Pop() methodunu inceleyelim ve stackın boş olduğu durumlarda default bir değer döndermek isteyelim:

public T Pop()
{
   m_StackPointer--;
   if(m_StackPointer >= 0)
   {
      return m_Items[m_StackPointer];
   }
   else
   {
      m_StackPointer = 0;
      return default(T);
   }
}

Referans tipleri için default değer null dır ve value tipleri için default değer 0 dır.

Çoklu Generic Tipleri

Örneğin aşağıdaki gibi bir generic bağlı liste düşünelim:

Kod Blok 3. Generic Bağlı Liste


class Node
{
   public K Key;
   public T Item;
   public Node NextNode;
   public Node()
   {
      Key      = default(K);
      Item     = defualt(T);
      NextNode = null;
   }
   public Node(K key,T item,Node nextNode)
   {
      Key      = key;
      Item     = item;
      NextNode = nextNode;
   }
}

public class LinkedList
{
   Node m_Head;
   public LinkedList()
   {
      m_Head = new Node();
   }
   public void AddHead(K key,T item)
   {
      Node newNode = new Node(key,item,m_Head.NextNode);
      m_Head.NextNode = newNode;
   }
}

Bağlı liste düğümleri (node) depolar:

class Node
{...}


Her bir düğüm bir anahtar (Generic tip parametresi K) ve bir değer tutar (Generic tip parametresi T). Aynı zamanda her bir düğüm bir sonraki düğüme referans verir. Bağlı listeler generic tip parametreleri K ve T ile tanımlanır:

public class LinkedList
{...}


Bu sayede AddHead() gibi generic methodlar ortaya çıkar:

public void AddHead(K key,T item);


Ne zaman generic tipinde bir değişken tanımladığınızda, kullanacağınız tipleri belirlemek zorundasınız. Ama belirlenen tip argümanları kendileride bir generic tip olabilirler. Örneğin, bağlı liste m_Head adında Node tipinde bir değişkene sahip. Bu listedeki ilk elemana referans veriyor. Aynı zamanda generic tipler olan K ve T ile tanımlanmış.


Node m_Head;


Bir düğümün instance ını oluştururken argümanların tipini belirtmelisiniz., bunun içinse bağlı listenin generic tiplerini kullanabilirsiniz:


public void AddHead(K key,T item)
{
   Node newNode = new Node<K,T>(key,item,m_Head.NextNode);
   m_Head.NextNode = newNode;
}

Not olarak söyleyelim liste ile node aynı isimlerde generic tipler kullanıyor. Bu sadece okunabilirliği kolaylaştırma açısından bu şekilde yazılmıştır. Aşağıdaki şekillerdede kullanılabilir:

public class LinkedList
{...}


Veya:

public class LinkedList
{...}


Listenin örnek kullanımı olarak key ler için integer value lar için string kullanmak istersek:


LinkedList list = new LinkedList();
list.AddHead(123,"AAA");


İsteyen başka şekillerdede kullanabilir:


LinkedList list = new LinkedList();
list.AddHead(DateTime.Now,"AAA");   


Bazen alias kullanmak faydalı olabilir.Bu şekilde bir kullanım sizi kod tekrarından kurtarabilir.

Kod Blok 4. Generic tip alias


using List = LinkedList;

class ListClient
{
   static void Main(string[] args)
   {
      List list = new List();
      list.AddHead(123,"AAA");
   }
}

2 Mayıs 2011 Pazartesi

C# Generic Sınıflara Giriş #1

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.

16 Nisan 2011 Cumartesi

Generic Sınıflar (C# Programlama Rehberi)

Generics, C# dili ve CLR (Common Language Runtime) 'nin  2.0 versiyonu ile geldi. Generics .NET Framework'ü, sınıf veya methodun tanımlanıp, client kodu tarafından başlatılana kadar bir veya birden fazla tipin tanımlanmasını erteleyen sınıf veya methodların tasarlanmasına olanak sağlayan, tip parametre konseptiyle, tanıştırdı. Örneğin bir generic tip pametresi T kullanarak, diğer clientın casting ve boxing operasyonlarının maliyetini ve risklerini düşünmeden kullanabileceği sınıflar yazabilirsiniz. Örnek kod aşağıdadır.


// Generic Classı tanımlayın
public class GenericList
{
void Add(T input) { }
}
class TestGenericList
{
private class OrnekSinif { }
static void Main()
{
// int tipinde bir liste tanımlayın
GenericList<int> list1 = new GenericList<int>();
// string tipinde bir liste tanımlayın
GenericList<string> list2 = new GenericList<string>();
// OrnekSinif tipinde bir liste tanımlayın
GenericList list3 = new GenericList();
}
}