C# IList Interface

Merhaba arkadaşlar,

Collections nesnelerini daha iyi anlamak için System.Collection isim alanında bulunan arayüzleri tanımaya devam ediyoruz. Bir önceki yazımda IDictionary arayüzünü incelemiştik. (IDictionary Interface yazıma buradan ulaşabilirsiniz)

Bir önceki yazımda neler yaptık hatırlamak amacıyla anımsayalım.

  • IDictionary arayüzü ve özellikleri
  • IDictionary arayüzünü uygulayarak anahtar-değer çiftine sahip kendi koleksiyon sınıfımızın geliştirilmesi
  • IDictionaryEnumerator ve IEnumerator arayüzlerinin uygulanması
  • DictionaryEntry sınıfının ne işe yaradığı ve özellikleri

Bu yazımda System.Collection isim alanında bulunun IList arayüzünü ele alıp, ne işe yaradığını ve bu arayüzü kullanarak kendi koleksiyon sınıfımızı inşa edeceğiz.Şimdi IList arayüzü hakkında neler biliyoruz ona bakalım. Bu arayüzü System.Array sınıfı ve List<T> sınıfı uygulamış.

Array ve şablon tipli List sınıfının özelliklerini aklınıza getirin. Benim aklıma gelen önemli özelliklerinden biri elemanlara indeks ile erişebilmemiz. Şimdi IList arayüzünün tanımına bakalım.

IList arayüzü, bir koleksiyondaki her bir elemana sıfır tabanlı bir indeks ile erişebilmek için çeşitli metotlar ve özellikler sunar. Ilist, ICollection ve IEnumerable arayüzünden türemiştir. (ICollection Interface yazıma burada ulaşabilirsiniz)

Tanımda elemanlara sıfır tabanlı indeks ile erişebiliriz. O zaman IList arayüzünün içinde bir indeksleyici (indexer) özelliği var. Bu bilgi burada dursun 🙂

IList arayüzünün özellikleri

  • Count özelliği ile koleksiyon içindeki eleman sayısını verir
  • Item[Int32] özelliği ile koleksiyondaki elemana erişebiliriz veya işlem yapabiliriz
  • IsFixedSize özelliği ile sabit boyuta sahip olup olmadığı
  • IsReadOnly özelliği ile koleksiyondaki elamanların salt okunur olup olmadığı

IList arayüzünün Metotları

  • Add(object) metodu ile koleksiyona eleman ekler
  • Clear() metodu ile koleksiyondaki elemanları temizler
  • Contains(object) metodu ile koleksiyonda ilgili elemanın olup olmadığı bilgisini verir
  • GetEnumerator() metodu ile koleksiyondaki elemanlara erişebilmemizi sağlar
  • CopyTo(Array, Int32) metodu ile belirli bir dizinden başlayarak hedef diziye koleksiyondaki elemanları kopyalar
  • IndexOf(Object) metodu ile koleksiyondaki elemanın dizinini belirtir
  • Insert(Int32, Object) metodu ile koleksiyona belirtilen dizin ile eleman ekler
  • Remove(Object) metodu ile koleksiyondan elemanı siler
  • RemoveAt(Int32) metodu ile koleksiyondan belirtilen dizi elamanı siler.

IList arayüzüne buradan daha detaylı bilgilere ulaşabilirsiniz.

Bu kadar teorik bilgi yeter 🙂 Bu edindiğimiz bilgileri ele alarak IList arayüzünü uygulayarak kendi koleksiyon tabanlı sınıfımızı inşa edelim. Konuyu daha iyi canlandırmak için şöyle bir senaryo geliştirelim; Yeni bir işe başladınız ve bu iş yerinde IList diye bir arayüz var ve bu arayüzü çok iyi dokümante etmişler. Bizden istedikleri bu arayüzü kullanarak bir grup nesnesi oluşturmak ve bu yapı ile grup nesnelerimizi yönetebilmek. (Eleman ekleme, çıkarma, güncelleme vs… gibi)

Geliştirmeye geçmeden önce IList arayüzünün IEnumerable arayüzünü uyguladığı dikkatinizi çekmiştir. Yani bu arayüz ile iterasyon niteliği kazanıp foreach döngüsü ile koleksiyondaki her bir elemana erişebileceğiz. (IEnumerable ve IEnumerator yazıma burada ulaşabilirsiniz)

Senaryoyu genişletelim. İlk başta bir sınıf oluşturuyoruz ve bu sınıfa IList özelliği kazandırıyoruz.

IList arayüzünü uyguladıktan sonra karşımıza çıkan özellikleri ve metotları uygulamamız gerekiyor. Bu özellik vey metotların bazıları farklı arayüzlerden gelmektedir. Özellik ve metotlara baktığımızda ekleme, çıkarma, içerme, koleksiyonun boyutu gibi özellikler var. Zaten bu özelliklere baktığımızda aslında koleksiyon olma mantığını görebiliyoruz.

Sınıfımızı oluşturduk ve IList arayüzünü uygulayarak uygulamamazı gereken özellik ve metotları öğrendik. Bu özellik ve metotlar tek başlarına anlamsız. bir koleksiyon nesnesi üzerinde işlem yapmalılar. O zaman farklı veri türlerini de içerebilen bir grup nesnesine yani koleksiyon nesnesine ihtiyacımız var. Burada Array yardımımıza koşuyor ve sınıfımıza elemanları tutabilecek object türünden bir dizi ekliyoruz. Bu diziyi sınıfımız oluşurken hazır duruma getirmeliyiz. Hazır hale getirebilmek için yapıcı metodun özelliğinden yararlanarak dizimizi sınıfımızdan bir nesne yarattığımızda oluşmasını sağlıyoruz.

Farklı veri türlerini saklayabileceğimiz object türünden bir dizimiz var ve nesnemiz oluşurken dizimiz varsayılan boyut kadar oluşuyor. Artık dizimiz hazır ve bu diziye eleman ekleyebiliriz. Bunun için Add metodunu uygulayacağız. Add metodu object türünden bir parametre aldığına dikkat edin 🙂 ve geriye int türünden bir değer dönüyor. Bu değer sayesinde koleksiyonumuza eleman eklenip eklenmediğini anlayacağız. Eğer eleman koleksiyona eklenirse geriye eklediğimiz elemanın indeks değerini döneriz bir şekilde ekleyemez isek geriye -1 değerini döneceğiz.

Yukarıda _count özelliği dikkatinizi çekmiştir. İhtiyaçlar çıktıkça ve programlama yaparken aklınıza birçok şey gelebilir. Add metodunu uygularken elemanı eklediğimizde eğer dizinin boyutu aşılırsa ne yapacağız? Koleksiyona eklenen elemanlarının sayısını tutan bir özelliğe ihtiyacımız oldu. O yüzden _count adında int türünde bir özellik ekliyoruz.

Elemanlarımızı ekleyebilir durumua geldik. İyi güzel bu elemanları ekliyoruz ama yanlış bir eleman ekledik diyelim. Şimdi eklediğimiz elemanları koleksiyondan çıkarmak için Remove metodunu uygulayacağız. Remove metoduna baktığımızda object türünde bir parametre alıyor ve void ile bildirilmiş yani geriye bir değer dönmüyor. Elemanı koleksiyondan silmek için silmek istediğimiz elamanın indeksine ihtiyacımız var. Silmek istediğimiz elamanın indeksini bulup daha sonra koleksiyondan onu silmeliyiz. İhtiyacımız olan şeyler indeks ve koleksiyondan silmek için bir algoritma 🙂 Yavaş yavaş bu metodu uygulamaya başlayalım.

İlk başta silmek istediğimiz elamanın indeksini buluyoruz. Silmek istediğimiz eleman için koşul koyuyoruz ve bu koşulu sağlıyorsa elemanı diziden siliyoruz. İki farklı yöntemle elemanı diziden çıkarabiliriz. İlk yaptığımız klasik sola kaydırma diğeri ise Array sınıfının Copy metodunu kullanarak sola kaydırmak. Array sınıfının Copy metodunu IDictionary Interface yazımda detaylı bir şekilde açıkladım isterseniz o yazıma buradan ulaşabilirsiniz.

Hazır elemanı diziden sildiğimize göre diziyi temizlemk isteyebiliriz. Bunu yapmak için Clear() metodunu uygulayacağız. Dizi içindeki her bir elamanı silmemiz gerekiyor ve count özelliğimizi ilk başa almalıyız. Burada elemanı silmek derken diziden çıkarmak değil dizi içindeki elemanları referanslarını temizlememiz gerekiyor.

Yukarıdaki kodu incelediğimizde aslında sadece _count özelliğini sıfırlasaydık yeterdi ama biz burada Bellekte yer tahsil edilmiş elamanların referanslarını da ilk değerine getiriyoruz. (object referansı kullandığımız için her bir elemanı null ile setliyoruz. null referans değeri bütün bitleri sıfırdır ve 32-bit işeltim sistemlerinde 4-byte, 64 bit işletim sistemlerinde 8-byte yer kaplar)

Koleksiyonda bir elemanaın var olup olmadığını kontrol etmek için Contains metodunu uygulayacağız. Contains metodu object türünden bir parametre alıyor ve geriye bool değerinde bir değer deniyor. Elemanın koleksiyonda olup olmadığını bulabilmek için koleksiyon içinde o elaman var mı diye kontrol etmemiz gerekiyor.

Contains metodu ile elamanın koleksiyonda olduğunu anlamak için bool değerlerine baktık. Peki o elemanın koleksiyondaki hangi indekste olduğunu bulmak isteyebiliriz. Bunun için IndexOf metodunu uygulayacağız. IndexOf metodu object türünde bir parametre alıyor ve geriye bulunan elemanın indeks değerini yada elaman bulunmadıysa -1 değerini döndüreceğiz. Hatırlarsanız Remove metodunda buna benzer bir yapı yapmıştık. Remove metodunda index bulup daha sonra elemanı koleksiyondan silmiştik. O zaman o kod bize burada yarayabilir ve kod tekrarından kaçınmış oluruz. Hadi şimdi hem IndexOf(object) metodunu uygulayalım hem de kodumuzu refactor edelim.

Şimdi ise bir indeksi belirterek koleksiyondan eleman silinmesini isteyebiliriz. Bunun için RemoveAt(int) metodunu uygulayacağız. RemoveAt(int) metodu int türünde bir parametre alır ve void ile bildirilmiştir. Silmek istediğimiz int için koşul koyacağız ve koşulu sağladıktan sonra o indeksi koleksiyondan çıkarıp elamanları sola doğru kaydıracağız. Aslında biz bunu Remove metodunda da yapmıştık. Güzel! oradaki kodu kullanıp hem kod tekrarından kaçınmış olur hem de kodumuzu tekrar refactor yapmış olacacağız.

Remove metodunun ne kadar kısaldığını fark etmişsinizdir. Programlama yaparken ihtiyaç doğrultusunda geliştirdiğimiz yapılarda geriye dönük düzenlemeler (refactor) yapabiliriz. Bu sayede hem kodun gereksiz yere çoklanmasını engelleriz hem de kodun okunabilirliği (readability) artırmış oluruz.

Add metodu ile koleksiyona elaman ekleyebiliyoruz artık şimdi ise belli bir indekse eleman eklemek isteyebiliriz. Bunun için Insert(int, object) metodunu uygulayacağız. Insert metodu iki parametre alıyor; birinci parametre elemanın hangi indekese eklenecek olması diğer parametre ise eklenecek olan elemanın değeri. Indeks ile eklemek için ilk başta kontrol koyacağız. Bu kontrol eklenecek olan eleman için kapsite var mı. Diğer bir kontrol indeks kontrolü ve koşul sağlandıktan sonra yeni eklenecek değeri ilgili indekse koymak ve diğer elemanları sağa doğru kaydırmak. Sağa doğru kaydırıyoruz çünkü koleksiyonumuzun boşta olan bitleri var.

Insert metodu ile indekse elamanı ekleyebiliyoruz. Eklenen elemanları bir başka grup nesnesine kopyalamak isteyebiliriz. Bunu için CopyTo(Array, int) metodunu uygulayacağız. Bu metot Array türünde bir dizi ve int türünde bir index parametresi alıyor. Array türündeki diziye koleksiyondaki elemanları aktaracağız ve bunu yaparken karşı tarafa hangi indeskten itibaren kopyalansın olan index ile yapacağız. Bunu yaparken iki yöntem ile yapabiliriz.

Koleksiyondaki elemanlara sıfır tabanlı indeks ile erişebiliriz diyordu IList tanımında. Bunu yapabilmek için indeksleyici metodunu uygualayacağız. (Indeksleyici ile ilgili yazıma buradan ulaşabilirsiniz). İndeksleyiciler (indexer) özel tanımlı olan özelliktir .İndeksleyici (indexer) sayesinde nesnemize bir indeks ile erişebilme imkanı sunar. Buda sınıfımızı bir diziymiş gibi kulanabilmemizi ve sınıfımızı dizinleyebilmek amacıyla kullanılır.

Indeksleyici index parametresi alır ve geriye tanımlanan nesneyi döner. Şimdi sınıfımızdaki indeksleyici ile koleksiyonlardaki elemanlara ulaşabilmeyi sağlayacağız. Aldığımız indeks değeri ile koleksiyonda karşılığı olan elemanı döneceğiz.

Sınıfımız indeksleyici ile koleksiyondaki elemanı dönebiliyor. Şimdi koleksiyondaki elemanların sayısını veren Current özelliğini uygulayacağız. Bu özellik aslında ilk başta koleksiyondaki elemanların sayısını tutmak için kullandığımız count özelliğini dönecek.

Refactor: Bununla birlikte sınıfımızda _count özelliğini kullanan yerleri Count özelliği ile değiştiriyorum.

Sınıfımız neredeyse hazır. Koleksiyona ekeldiğimiz elemanları foreach döngüsünde kullanmak ve her bir elemana erişip işlem yapmak için IEnumerable arayüzünün metodu olan GetEnumerator() metodunu uygulamamız gerekiyor. GetEnumerator() metodu geriye IEnumerator türünde bir Enumerator bekliyor. (IEnumerable ve IEnumerator Iinterface yazıma buradan ulaşabilirsiniz). O halde bizim bir Enumerator yapısına ihtiyacımız var. Aslında bunu yeni bir iterasyon tekniği olan yield anahtar sözcüğü ile yapabiliriz ama şu an kendi sınıfımızı inşa edeceğiz. Hadi şimdi IEnumerator arayüzünü uyguladığımız sınıfımız inşa edelim.

GenericListEnumerator adında bir sınıf oluşturuyorum ve IEnumerator arayüzünü uyguluyorum. Özellikleri ve metotları uyguluyoruz.

GenericListEnumerator sınıfımız hazır artık bu sınıfımızı GenericList sınıfımıza bildirmemiz gerekiyor. Yani GenericList sınıfına sen Enumerator olarak benim inşa ettiğim Enumerator’ü kullanacaksın diyeceğiz.

Koleksiyon sınıfımız artık hazır. Bu sınıfı kullanarak farklı veri türünden nesnelerimizi ekleyebilir, silebilir, güncelleyebilir ve bu elemanlar arasında ilerleyebiliriz. Şİmdi bu sınıfımız kullanarak farklı veri türünde nesnelerimizi ekleyip ekrana yazdıralım. Bunun için console uygulamasını kullanacağız.

Kodun son hali

System.Collection isim alanında kendi çalışma prensiplerine uygun göre olan birçok arayüzü var. Bu yazımızda System.Collection isim alanında bulunan IList arayüzünü uygulayarak kendi koleksiyon sınıfımızı geliştirdik. Böylelikle bir koleksiyon tabanlı sınıfta hangi metot ve özellikler olmalı ve bu özellikler ve metotlar ne işe yarıyor öğrenmiş olduk.

Bir sonraki yazımda System.Collection isim alanında bulunan temel koleksiyon sınıflarını inceleyeceğiz. Bu sınıfların avantajları, dezavantajları ve kullanabilirliğini ele alacağız. Bir sonraki yazımızda görüşmek üzere 🙂

Kaynaklar:

Her Yönüyle C# 7.0, Sefer Algan, Pusula Yayincilik

https://docs.microsoft.com/tr-tr/dotnet/api/system.collections.ilist?view=netframework-4.8

https://docs.microsoft.com/tr-tr/dotnet/csharp/programming-guide/concepts/collections

Leave a Comment