Makale Özeti

Her ne kadar ArrayList, Stack ve Queue gibi sınıflar nesne koleksiyonlarını saklamak için çoğu zaman ihtiyaçlarımıza karşılık verseler de bazı durumlarda, sadece belli veri tiplerinin saklanmasına izin veren koleksiyonlara ihtiyacımız olabilir. Bu gibi durumlarda ArrayList sınıfı ile aynı isim uzayı içerisinde bulunan CollectionBase sınıfından faydalanarak işimizi oldukça kolaylaştırabiliriz.

Makale

Her ne kadar ArrayList, Stack ve Queue gibi sınıflar nesne koleksiyonlarını saklamak için çoğu zaman ihtiyaçlarımıza karşılık verseler de bazı durumlarda, sadece belli veri tiplerinin saklanmasına izin veren koleksiyonlara ihtiyacımız olabilir. Bu gibi durumlarda ArrayList sınıfı ile aynı isim uzayı içerisinde bulunan CollectionBase sınıfından faydalanarak işimizi oldukça kolaylaştırabiliriz.

Öncelikle CollectionBase sınıfının yapısını biraz inceleyelim;

CollectionBase sınıfı soyut (abstract) bir sınıftır. Dolayısıyla bu sınıfın bir örneğini oluşturup kullanmamız mümkün değil. Kendi koleksiyonumuzu oluşturmak için ilk yapmamız gereken şey, yeni bir sınıf tanımlamak ve bu sınıfı CollectionBase sınıfından türetmek.

CollectionBase, koleksiyona öğe eklemek, çıkarmak ve belirtilen bir nesnenin koleksiyonda bulunup bulunmadığını öğrenebilmek için bir takım metodlar içerir. Birazdan teker teker uygulayacağımız bu metodların tümü IList arayüzüne ait metodlardır.  CollectionBase sınıfından türettiğimiz koleksiyon sınıflarında, saydığımız işlemleri gerçekleştirebilmek için, CollectionBase’in List özelliğinden faydalanılır. List özelliği sayesinde yeni bir koleksiyon oluşturmak gerçekten çok kolaydır.

Örneğimizde, içerisinde sadece Kitap sınıfından oluşturulmuş kitap nesnelerinin saklanmasına izin veren yeni bir koleksiyon oluşturacağız. Daha sonra bu koleksiyona farklı şekilde kitap nesneleri ekleyen, kitap ismine göre koleksiyon içerisinde tarama yapan, standart koleksiyonlarda bulunmayan yeni metodlar tanımlayacağız.

Koleksiyon sınıfımızı oluşturmadan önce, koleksiyon içerisinde saklayacağımız Kitap nesnelerin tanımını yapalım.

using System;

namespace Koleksiyonlar {

     /// <summary>

     /// KitapKoleksiyonunu test etmek için

     /// tanımladığımız özel sınıf.

     /// </summary>

     public class Kitap      {

         public Kitap() {}

         public Kitap(string KitapAdi, string Yazar, DateTime BasimTarihi) {

              this.KitapAdi = KitapAdi;

              this.Yazar = Yazar;

              this.BasimTarihi = BasimTarihi;

         }

         public string KitapAdi;

         public string Yazar;

         public DateTime BasimTarihi;

     }

}

Koleksiyon içerisinde saklanacak Kitap nesnelerinin tanımı

Oluşturacağımız KitapKoleksiyonu adındaki koleksiyon, içerisinde sadece yukarıda tanımladığımız Kitap sınıfından oluşturulmuş nesnelerin saklanmasına izin verecek. Farklı bir veri tipinde nesne eklenmek istenirse bir hata (exception) fırlatılacak.

Şimdi adım adım koleksiyonumuzu oluşturalım;

İlk yapmamız gereken, bir sınıf tanımlamak ve bu sınıfı CollectionBase’den türetmek:

using System;

using System.Collections;

namespace Koleksiyonlar {

     public class KitapKoleksiyonu : CollectionBase {

         public KitapKoleksiyonu() {}

         }

}

KitapKoleksiyonu sınıfının tanımı

Sınıf tanımını yaptıktan sonra, koleksiyona işlerlik kazandırmak için IList arayüzü içerisinde tanımlanan ve CollectionBase’in List özelliği ile bize sunulan metodları uygulamaya başlayabiliriz.

Uygulayacağımız ilk metod bir koleksiyona öğe eklenmesini sağlayan IList.Add metodu:

public int Add(Kitap value) {

     return (List.Add(value));

}

KitapKoleksiyonu.Add

Koleksiyona öğe eklemek için gerekli olan Add metodunun tanımını yaptık. Metodun ne kadar kısa olduğunu farketmişsinizdir. Belirtilen öğenin listeye eklenmesi için CollectionBase tarafından bize devredilen List özelliğinin Add metodunu kullanmamız yeterli. List özelliği öğenin saklanması işini bizim için hallediyor. Burada dikkat edilmesi gereken nokta, Add metodunun parametresinde eklenecek olan öğenin veri tipini ArrayList ve diğer koleksiyonlarda olduğu gibi object olarak tanımlamamış olmamız. Bizim yaptığımız tanımda value parametresi Kitap veri tipinde. Böylece daha en baştan, koleksiyona veri tipi Kitap olmayan nesnelerin eklenmesini engellemiş oluyoruz.

Uygulayacağımız bir sonraki metod IList arayüzünün Contains metodu:

public bool Contains(Kitap value) {

     return (List.Contains(value));

}

 KitapKoleksiyonu.Contains

Contains metodu value parametresinde belirtilen bir kitap nesnesinin koleksiyon içinde bulunup bulunmadığını bildiriyor. Anlaşıldığı gibi, eğer parametrede belirtilen kitap nesnesi eğer daha önce Add metodu ile koleksiyona eklenmişse metod True döndürüyor. Burada da List özelliğinden faydalandık. CollectionBase.List gerekli olan işlemleri bizim için yine hallediyor.

Bir sonraki metod IList.IndexOf metodu:

public int IndexOf(Kitap value) {

     return (List.IndexOf(value));

}

KitapKoleksiyonu.IndexOf

ArrayList üzerinde sıkça kullandığımız bu metod, parametrede geçilen nesnenin koleksiyon içerisindeki sırasını (indeksini), eğer belirtilen nesne koleksiyonda yoksa “-1” döndürüyor.

public void Insert(int index, Kitap value) {

     List.Insert(index, value);

}

KitapKoleksiyonu.Insert

Koleksiyonumuzun Insert metodu, koleksiyon içerisine istediğimiz bir pozisyona bir kitap nesnesi eklememizi sağlıyor.

public void Remove(Kitap value) {

     List.Remove(value);

}

KitapKoleksiyonu.Remove

Remove metodu belirtilen bir kitap nesnesinin koleksiyondan atılmasını gerçekleştiriyor.

Koleksiyonda sakladığımız Kitap nesnelerine indeks numarası belirterek ulaşabilmek için koleksiyonumuzda indeksleyiciler tanımlamamız gerekiyor:

public Kitap this[int index] {

     get {

         return ((Kitap) List[index]);

     }

     set {

         List[index] = value;

     }

}

KitapKoleksiyonu indeksleyici tanımı

Yukarıdaki indeksleyici tanımı sayesinde, indeks belirtilerek koleksiyon içerisinde saklanan kitap nesnelerine ulaşmak mümkün hale geliyor. Yaptığımız tek şey List içerisinden belirtilen indekse sahip nesneyi çekmek, onu Kitap nesnesine dönüştürmek ve geri döndürmek. Belirtilen indeksin limitler içerisinde olup olmadığını kontrol etmemize ayrıca gerek yok; çünkü List, bu kontrolü zaten gerçekleştiriyor ve eğer belirtilen indeks koleksiyon içerisindeki nesne sayısından fazla ise bir hata (exception) fırlatılıyor.

Buraya kadar yaptığımız herşey standart bir koleksiyon içerisinde olan metodları tanımlamaktan ibaretti. Bu metodları oluştururken sürekli olarak List özelliğinden faydalandık. List, arka planda yapılması gereken herşeyi bizim için gerçekleştirdi. ArrayList’ten farklı olarak bizim koleksiyonumuz, listesine, sadece Kitap sınıfından oluşturulmuş nesnelerin eklenmesine izin veriyor. Bunu da Add, Insert, Remove metodlarında parametreleri Kitap veri tipinde tanımlayarak gerçekleştirdik.

Madem koleksiyonumuz sadece Kitap nesnelerini saklayacak, öyleyse Add, Contains, Insert metodlarının isteğimize göre özelleştirdiğimiz versiyonlarını tanımlayabiliriz. Ayrıca kitap adına göre indeksleme yapabilir, IndexOf metodunu kitap ismine göre indeks numarası döndürecek hale getirebiliriz.

Add metodundan başlayalım:

public int Add(string KitapAdi, string Yazar, DateTime BasimTarihi) {

     return (List.Add(new Kitap(KitapAdi, Yazar, BasimTarihi)));

}

KitapKoleksiyonu.Add

Add metodunun aşırı yüklenmiş (! overloaded) versiyonu, Kitap sınıfının tanımına uygun parametreler alıyor, yeni bir kitap nesnesi oluşturuyor ve oluşturduğu kitap nesnesini koleksiyona ekliyor. Böylece Add metodunun iki versiyonunu yazmış olduk. Birincisi listeye eklemek için parametre olarak bir kitap nesnesi kabul ediyor, diğeri ise KitapAdi, Yazar ve BasimTarihi parametrelerini kullanarak kitap nesnesini kendisi oluşturup listeye ekliyor.

Aynı işlemi Insert metodunda da kullanabiliriz:

public void Insert(int index, string KitapAdi, string Yazar, DateTime BasimTarihi) {

     List.Insert(index, new Kitap(KitapAdi, Yazar, BasimTarihi));

}

KitapKoleksiyonu.Insert

Insert metodu da Add ile aynı şekilde, aldığı parametrelere göre yeni bir kitap nesnesi oluşturuyor ve koleksiyona ekliyor.

Contains, IndexOf ve Remove metodlarını kitap adına göre çalışan indeksleyicimizi yazdıktan sonra uygulayacağız. Önce indeksleyicimizi tanımlayalım:

public Kitap this[string kitapAdi] {

     get {

         foreach (Kitap kitap in List) {

              if (kitap.KitapAdi == kitapAdi)

                   return kitap;

         }

         return ((Kitap) null);

     }

     set {

         Kitap kitap = this[kitapAdi];

         if (kitap != null)

              List[IndexOf(kitap)] = value;

     }

}

Kitap adına göre çalışan indeksleyici

Indeksleyicimiz kitap adına göre çalıştığından get metodu koleksiyon içerisinde foreach ile bir tarama gerçekleştiriyor. Eğer koleksiyon içerisinde belirtilen isimde bir kitap bulursa, bulduğu kitap nesnesini döndürüyor. Eğer kitapAdi parametresi ile geçilen isimde bir kitap bulunamazsa, indeksleyici null döndürüyor.  Bu arada şunu da eklemekte fayda var; Koleksiyon içerisinde eğer aynı isimde birkaç kitap varsa, indeksleyici sadece ilk rastladığı kitabı döndürüyor.

Indeksleyicinin set metodu, get metodundan faydalanıyor. Set metodu önce kitapAdı parametresindeki isme sahip kitabı kapalı bir şekilde get erişim metodu ile çekiyor. Çekilen kitap nesnesi null değilse, arka plandaki List nesnesine ulaşarak, get ile bulunan kitabı value parametresi ile gelen kitapla değiştiriyor.

İndeksleyiciyi oluşturduğumuza göre Contains, IndexOf ve Remove metodlarını indeksleyiciyi kullanarak uygulayabiliriz. Önce Contains metodu:

public bool Contains(string KitapAdi) {

     return (this[KitapAdi] != null);

}

KitapKoleksiyonu.Contains

Contains metodu isme göre çalışan indeksleyiciden faydalanıyor. Indeksleyiciyi kullanarak, belirtilen isimde bir kitap bulunamaz ise false döndürüyor.

public int IndexOf(string KitapAdi) {

     return (List.IndexOf(this[KitapAdi]));

}

KitapKoleksiyonu.IndexOf

IndexOf metodu da indeksleyiciyi kullanarak, önce belirtilen isme göre kitaba ulaşıyor, daha sonra List üzerinde IndexOf metodunu çağırarak kitabın indeksini döndürüyor.

public void Remove(string kitapAdi) {

     List.Remove(this[kitapAdi]);

}

KitapKoleksiyonu.Remove

Remove metodu yine indeksleyici üzerinde belirtilen isimdeki kitabı buluyor ve List.Remove metodu ile kitabı koleksiyondan siliyor.

KitapKoleksiyonu üzerinde böylece tüm işlemleri tamamlamış olduk. Koleksiyonumuz artık sadece Kitap nesneleri ile çalışan, ve hatta kitap ekleme ve silme işlemlerini sadece indeks numaraları üzerinden değil, kitap isimlerine göre de yapabilen bir koleksiyon haline geldi.

Koleksiyonumuza Kitap nesneleri dışında hiçbir nesnenin eklenemeyeceğini söylemiştik, bir istisna dışında; Kitap sınıfından türetilmiş sınıfların örnekleri koleksiyona eklenebilir.

Örneğin:

public class YemekKitabi : Kitap {

}

Kitap sınıfından türetilmiş YemekKitabi sinifi

şeklinde tanımını yaptığımız yeni sınıftan oluşturulan nesneler koleksiyona eklenebilir. Eğer bunu da engellemek istiyorsak CollectionBase’in OnInsert ve OnSet metodlarının üzerine yazmamız gerekir. Bu iki metod koleksiyona bir nesne eklendiğinde ve indeksleyici kullanılarak koleksiyondaki bir nesne bir başkası ile değiştirildiğinde çağırılır. Bu iki metodun üzerinde yazarak, koleksiyona eklenen nesnenin veri tipini kontrol edebiliriz:

protected override void OnInsert(int index, object value) {

     if (value.GetType() != Type.GetType("Koleksiyonlar.Kitap"))

          throw new ArgumentException("Listeye Kitap sınıfından oluşturulmuş nesneler eklenebilir.", value");

     }

KitapKoleksiyonu.OnInsert

OnInsert metodu içerisinde listeye eklenmek istenen nesnenin veri tipini kontrol ediyoruz. Eğer veri tipi Koleksiyonlar.Kitap veri tipinden farklı ise bir ArgumentException fırlatıyoruz. Böylece Insert metodunun çağırılmasının önüne geçmiş ve nesnenin koleksiyona eklenmesini engellemiş oluyoruz.

Aynı işlemi OnSet metodu için tekrar edeceğiz:

protected override void OnSet(int index, object oldValue, object newValue) {

     if (newValue.GetType() != Type.GetType("Koleksiyonlar.Kitap"))

         throw new ArgumentException("Listeye Kitap sınıfından oluşturulmuş nesneler eklenebilir.",                                            “newValue");

}

KitapKoleksiyonu.OnSet

Böylece koleksiyona Kitap nesnelerinden başka nesnelerin eklenmesini imkansız hale getirmiş olduk.

Koleksiyonumuz kullanılmaya hazır...

Kodları toparlarsak;

KitapKoleksiyonu.cs

using System;

using System.Collections;

namespace Koleksiyonlar {

     public class KitapKoleksiyonu : CollectionBase {

         public Kitap this[int index] {

              get {

                   return ((Kitap) List[index]);

              }

              set {

                   List[index] = value;

              }

         }

         public Kitap this[string kitapAdi] {

              get {

                   foreach (Kitap kitap in List) {

                        if (kitap.KitapAdi == kitapAdi)

                            return kitap;

                   }

                   return ((Kitap) null);

              }

              set {

                   Kitap kitap = this[kitapAdi];

                   if (kitap != null)

                        List[IndexOf(kitap)] = value;

              }

         }

         public int Add(Kitap value) {

              return (List.Add(value));

         }

         public int Add(string KitapAdi, string Yazar, DateTime BasimTarihi) {

              return (List.Add(new Kitap(KitapAdi, Yazar, BasimTarihi)));

         }

         public bool Contains(Kitap value) {

              return (List.Contains(value));

         }

         public bool Contains(string KitapAdi) {

              return (this[KitapAdi] != null);

         }

         public int IndexOf(Kitap value) {

              return (List.IndexOf(value));

         }

         public int IndexOf(string KitapAdi) {

              return (List.IndexOf(this[KitapAdi]));

         }

         public void Insert(int index, Kitap value) {

              List.Insert(index, value);

         }

         public void Insert(int index, string KitapAdi, string Yazar, DateTime BasimTarihi) {

              List.Insert(index, new Kitap(KitapAdi, Yazar, BasimTarihi));

         }

         public void Remove(Kitap value) {

              List.Remove(value);

         }

         public void Remove(string kitapAdi) {

              List.Remove(this[kitapAdi]);

         }

         protected override void OnInsert(int index, object value) {

              if (value.GetType() != Type.GetType("Koleksiyonlar.Kitap"))

                   throw new ArgumentException("Listeye sadece Kitap nesneleri eklenebilir.", "value");

         }

         protected override void OnSet(int index, object oldValue, object newValue) {

              if (newValue.GetType() != Type.GetType("Koleksiyonlar.Kitap"))

                   throw new ArgumentException("Listeye sadece Kitap nesneleri eklenebilir.", "newValue");

         }

     }

}

Kitap.cs

using System;

namespace Koleksiyonlar {

     public class Kitap      {

         public Kitap() {}

         public string KitapAdi;

         public string Yazar;

         public DateTime BasimTarihi;

         public Kitap(string KitapAdi, string Yazar, DateTime BasimTarihi) {

              this.KitapAdi = KitapAdi;

              this.Yazar = Yazar;

              this.BasimTarihi = BasimTarihi;

         }

     }

}

KoleksiyonTest.cs

using System;

namespace Koleksiyonlar {

     public class KoleksiyonTest {

         public KoleksiyonTest() {}

         [STAThread]

         public static void Main() {

              // Tanımladığımız koleksiyondan bir nesne oluşturuyoruz...

              KitapKoleksiyonu koleksiyon = new KitapKoleksiyonu();

              // Koleksiyona kitap nesneleri ekliyoruz...

              Kitap kitap1 = new Kitap("Kitap 1", "Yazar 1", new DateTime(1970, 1, 1));

              koleksiyon.Add(kitap1);

              koleksiyon.Add("Kitap 2", "Yazar 2", DateTime.Now);

              Console.WriteLine("Koleksiyondaki kitaplar:");

              foreach (Kitap kitap in koleksiyon) {

                   Console.WriteLine("Kitap Adı:{0}\nYazar:{1}", kitap.KitapAdi, kitap.Yazar);

              }

              // Indeksleyici kullanarak kitap nesnesine ulaşıyoruz...

              Console.WriteLine("Indeksleyici ile erişim.");

              Console.WriteLine(koleksiyon[0].KitapAdi);

              // Kitaba indeksleyici ile kitabın ismini kullanarak ulaşıyoruz...

              Console.WriteLine(koleksiyon["Kitap 2"].Yazar);

              // Kitaplardan birini siliyoruz...

              koleksiyon.Remove("Kitap 2");

              Console.WriteLine(koleksiyon.Count);

         }

     }

}