C# IEnumerable, IEnumerator Ara yüzleri ve Kullanımı

C#'ta iterator kavramını düşündüğümüzde tahmin ediyorum ki aklınıza gelen ilk yapı foreach yapısıdır. foreach ile bir koleksiyon ya da array üzerinde gezinerek elemanlarına ulaşabiliyoruz. foreach arkaplanda iterator yapısını kullanır.

Bugün inceliyeceğimiz konu biz yazdığımız bir sınıf içerisindeki elemanları foreach ile gezmek istesek ne yapmamız gerekiyor? Örneğin AlisverisSepeti diye bir sınıf oluşturduk bu sınıfın içerisinde Urun nesneleri var bu nesneleri foreach ile itere etmek istiyoruz ne yapmamız gerekiyor?

İlk aklımıza gelen şekilde programımızı bir yazalım:

    public class Urun
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

    public class AlisverisSepeti
    {
        public Urun[] UrunListesi { get; set; }

        public AlisverisSepeti()
        {
            UrunListesi = new Urun[] 
            { 
                new Urun(){ Name="Bilgisayar", Price=1022.3m},
                new Urun(){ Name="Ipad", Price=699.9m}
            };
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            AlisverisSepeti sepetim = new AlisverisSepeti();

            foreach (Urun urun in sepetim)
            {
                Console.WriteLine("Urun: {0}, Fiyat: {1}"
                       , urun.Name, urun.Price); 
            }
        }
    }


Gördüğünüz gibi bir AlisverisSepeti sınıfı oluşturduk ve bu sınıf içerisindeki Urun listesine bir takım ürünler attık. Daha sonra ise alisveris sepetimizi foreach ile gezmeye çalıştık ama bu durumda bu mümkün değil. Böyle bir tasarım yaparsak aşağıdaki hatayı alırız:

foreach statement cannot operate on variables of type 'IteratorsExample.AlisverisSepeti' because 'IteratorsExample.AlisverisSepeti' does not contain a public definition for 'GetEnumerator'

Bu sınıfımızın iterator özelliği kazanması için IEnumerable veya IEnumerable<T> interfacelerini implemente etmesi gerekmektedir. Bu interface, uygulandığı sınıfa GetEnumerator adlı bir metot kazandırır ve bu metot geriye IEnumerator interface’ini implement eden bir sınıf döndürür. Bir sınıf üzerinde foreach ile dönebilmemiz için bu sınıfın GetEnumerator metodunun olması gerekmektedir.

Şimdi AlisverisSepeti sınıfımızı IEnumerable interface'ini implemente edecek şekilde değiştirelim:

    class AlisverisSepeti:IEnumerable
    {
        public Urun[] UrunListesi { get; set; }

        public AlisverisSepeti()
        {
            UrunListesi = new Urun[] 
            { 
                new Urun(){ Name="Bilgisayar", Price=1022.3m},
                new Urun(){ Name="Ipad", Price=699.9m}
            };
        }

        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

Gördüğünüz gibi IEnumerable interface'ini Alisveris sınıfımıza implemente ettiğimizde IDE'miz bize IEnumerator döndüren GetEnumerator isimli bir metot yazmamış gerektiğini söyledi. Örneğimizde bu metodu yazabilmemiz için IEnumerator interface'ini implemente etmiş AlisverisSepetiEnumerator sınıfını yazmamız gerekecek. IEnumerator interface'i ise uygulandığı sınıfa MoveNext, Reset adlı bir metotları ve readonly Current property'sini kazandırır.

    class AlisverisSepetiEnumerator:IEnumerator
    {
        public object Current
        {
            get { throw new NotImplementedException(); }
        }

        public bool MoveNext()
        {
            throw new NotImplementedException();
        }

        public void Reset()
        {
            throw new NotImplementedException();
        }
    }

 Şimdi bu implementasyonumuzu gerçekleştirelim:

    class AlisverisSepetiEnumerator:IEnumerator
    {
        private Urun[] _UrunList;
        int _curentIndex = -1;

        //itere edilecek liste aliniyor
        public AlisverisSepetiEnumerator(Urun[] urunList)
        {
            _UrunList = urunList;
        }

        //iterasyonda kalinan yer
        public object Current
        {
            get { return _UrunList[_curentIndex]; }
        }

        //bir sonraki adim var mi kontrolu
        public bool MoveNext()
        {
            return ++_curentIndex < _UrunList.Length;
        }

        //iterasyon basa alinir
        public void Reset()
        {
            _curentIndex = -1;
        }
    }

AlisverisSepeti sınıfımız için custum bir Enumerator yazdığımıza göre artık GetEnumerator metodumuzu düzenleyebiliriz:

        public IEnumerator GetEnumerator()
        {
            return new AlisverisSepetiEnumerator(UrunListesi);
        }

Bu son değişikliğin ardında artık AlisverisSepeti sınıfımız itere edilebilir hale geldi programımızın çıktısı şu şekilde olacak:


Bu programda kullandığımız sepetimizdeki urunleri tutan Urun[] collection'ı itere edilebilir bir yapıda olduğu için kendi içerisinde tanımlanmış GetEnumerator metodu bulunmaktadır. Bu yüzden manuel olarak Enumerator sınıfı yazmak yerine UrunListesi.GetEnumerator() şeklinde çağırarak bu iterasyon işlemini gerçekleştirebilirdik. Yukarıdaki örneğimizi bu metodu olmayan nesneleri manuel nasıl itere edebileceğimizi göstermek için verdik. Daha kolay bir şekilde şöyle gerçekleştirebilirdik:

        public IEnumerator GetEnumerator()
        {
            return UrunListesi.GetEnumerator();
            //return new AlisverisSepetiEnumerator(UrunListesi);
        }

IEnumerator interface'inin birde generic hali IEnumerator<T> bulunmakta bunu ilerleyen yazılarımızda inceleyeceğiz.

Örnek Proje: IEnumeratorExample.rar

0 yorum :

Yorum Gönder