5 Şubat 2011 Cumartesi

C#'da Foreach Iterasyonu

Uygulama gelistirirken, belli bir dizinin veya koleksiyonun elemanlari icinde gezerken foreach kullaniriz. Foreach ile ekrana yazdirma, deger atama ... vs. gibi yapmak istedigimiz isleri yapariz. Buraya kadar her sey cok normal, direk yazip geciyoruz. Ama, aslinda bilmeyiz ki, foreach ile bu isleri yaparken, arkada IEnumerable interface'sini implemet ediyor, bunun sonucunda IEnumerator tipinden bir GetEnumerator metodunu override etmemizi istiyor ve bu GetEnumerator de kendi icindeki uyelerle bizim foreach ile yaptigimiz isleri yapiyor.

Buraya kadar her sey cok karisik gelmistir. Ufak bir senaryo uzerinden daha iyi anlayacagiz simdi. string,int,ArrayList gibi tipler IEnumerable'i implement ettiginden foreach ile islem yapilmaya uygundurlar. Ya bizim elimizde, bu interface'yi implement etmeyen bir class bulunursa? Iste asil olay burada basliyor.

Diyelim ki elimizde Personel sinifi bulunsun. Personel'in Id'si,Adi ve Maasi bulunsun. Personel nesnelerimiz de su sekilde bulunsun.

Id;Ad;Maas

1;Ali;100
2;Veli;200
3;Deli;300

Bu personel nesnesinin icinde gezip Id'lerini ekrana yazdirmak istersek, basitce su foreach yapisini yazmamiz gerekirdi.



            Personel prs = new Personel();

            foreach (int id in prs)
            {
                Console.WriteLine(id);
            }


Bu islemi yapip calistirdigimizda su hatayi aliriz;

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

Bu hatanin sebebi, Bizim yazdigimiz Personel sinifinin IEnumerable interfacesini implement etmeyisidir. Bunun uzerine sinifimiza gelip IEnumerable interfacesini uygularsak, bizden GetEnumerator metodunun icini doldurmamizi ister ve bunun sonucunda foreach ile gezme islemini yapabilmis oluruz.

Fakat bizim senaryomuz, id'ler uzerinden islem yapmak oldugundan; biz once tum personelleri tutup IEnumerable interfacesini implement eden bir sinif yazacagiz, daha sonra da Id'ler uzerinden islem yapan ve IEnumerator interfacesini implement eden bir class yazacagiz.

Bu ana kadar IEnumerator hakkinda konusmadik. IEnumerator interfacesi, IEnumerable interfacesinin bir sinifa uygulandiktan sonra ici doldurulmasi istenen metodun(GetEnumerator) geri donus tipidir. Gorevi,iterasyonu saglamaktir. IEnumerator'un icinde MoveNext,Current ve Reset uyelerini bulundurur. Bu uyelerin iclerini doldurdugumuzda artik bizim yazdigimiz sinif da foreach ile iterasyon yapilabilecek duruma gelecektir.

Ornegimize geri donersek;
PersonelYonetim isminde  bir class yaratip IEnumerable interfacesini implement edersek, bizden IEnumerator tipinden GetEnumerator metodunu doldurmamiz istenecek. Geri donus tipi IEnumerator olmasi, bizim o metotdan IEnumerator interfacesini implement eden herhangi bir uye geri donderebilecegimizi gosterir. O zaman biz de kendi uyemizi kendimiz yaratalim. Bir class acip, IEnumerator interfacesini uygulayip, MoveNext,Current ve Reset gibi uyelerin iclerini dolduralim.

Yeni yazacagimiz class IdNumarator isminde olsun ve IEnumerator interfacesini uygulasin. Uyguladiktan sonra bizden bazi uyelerin iclerini doldurmamizi isteyecek. Nedir bu uyeler ve uyelerin gorevleri;

Current : O anki koleksiyondaki deger geri doner.
Reset : Sayaci -1'e dondurur.
MoveNext: Siradaki elemana ulasmamizi saglar. Sonraki eleman yoksa da false doner, ulasiyorsak ture doner.


class IdNumarator:IEnumerator
    {
        Personel[] _prs; //Personellerimizi bir dizi icerisinde tutalim
        int _sayac;

        public IdNumarator(Personel[] prs)
        {
            _prs = prs;
            Reset();
        }

        public object Current
        {
            get { return _prs[_sayac].Id; } //O anki personelin id'sini geri dondurelim.
        }

        public bool MoveNext()
        {
            _sayac++;
            return _sayac < _prs.Length; //dizinin boyutunu asmamaliyiz.
        }

        public void Reset()
        {
            _sayac = -1; //Once  movenext calisacagindan, sayaci -1 yapmaliyiz, cunku dizi[0] 'dan baslamali.
        }
    }



Simdi yapmamiz gereken tek sey, PersonelYonetim sinifindaki GetEnumerator metodunda bu classimizi orneklemek (Hatirlatma: GetEnumerator metodu, IEnumerator tipini implement eden herhangi bir uyeyi geri donderdiginden, bizim classimiz da IEnumerator'u implement ettiginden kullanabiliriz.)


Personel[] prs;

public IEnumerator GetEnumerator()
        {
            return new AdNumarator(prs);
        }


Tum bu islemlerden sonra, artik biz de Personel sinifimizda foreach ile Id'lerde gezebilecegiz.


            PersonelYonetim personel = new PersonelYonetim(); 
            foreach (int id in personel)
            {
                Console.WriteLine(id);
            }



NOT: Iterasyonlar, belli bir kurala gore bir nesneye ulasmaktir. Dolayisiyla foreachde de her bir iterasyon kurali, bizim icin IEnumerator interfacesini implement eden bir class'tir.(Hatirlatma:Bu makaledeki ornekte, IdNumarator'dur.)

0 yorum: