3 Eylül 2012 Pazartesi

C# 'da Döngüler - FOREACH Döngüsü

C sharp ‘da her hangi bir koleksiyon tipinin elemanlarını teker teker bir döngüde ele alabilir, istersek bu elemanlara değer atayabilir (set), istersek de bu elemanları okuyabiliriz (get). Bu işlemler için en ideal döngü Foreach döngüsüdür ki: kelime anlamı da her biri demektir. Biz de koleksiyon içerisindeki her bir eleman için dön tabirini kullanacağız. For döngüsünden farklı olarak her hangi bir iteration kullanmayız fakat arka planda kendisi bir sonraki elemana geçer. Bu yüzden foreach ile yapılabilen her şey for döngüsü ile de yapılabilir. Fakat for döngüsü ile yapılan her şey foreach döngüsü ile yapılır dersek yalan söylemiş oluruz.

Söz dizimine (syntax ‘ına) bakacak olursak, öncelikle koleksiyon içerisindeki her bir elemanın tipi yazılır. Daha sonra ilgili koleksiyonun her bir elemanına, foreach döngüsü içerisinde hangi isimle sesleneceğimiz belirten bir değişken adı belirtilir. Daha sonra “in” anahtar sözcüğü kullanılarak hangi koleksiyonun elemanlarında dönecek isek o koleksiyonun adı yazılır. Aşağıda bir örnek bulunmaktadır.
     
     foreach (elemanTipi değiskenAdı in koleksiyonAdi)
            {   
            }

Foreach Döngüsü ile koleksiyon içerisindeki her bir elemanın değerini okuma :

Hemen bir örnek üzerinde foreach döngüsü ile bir koleksiyon içerisindeki elemanları okumayı inceleyelim. “Sehirler” adında string bir dizimiz olsun ve bu dizi elemanlarını bir kontrolde teker teker gösterelim. Bir “Windows Form Application” açalım. Formumuza bir adet ListBox kontrolü bırakalım. ListBox ‘ın ismini de “lstOgreler” olarak değiştirelim. Kodlarımızı formun Load event ‘ine yazalım.

     string[] sehirler = { "Adana", "Adıyaman", "Afyon", "Ağrı", "Amasya", "Ankara", "Antalya" };

            foreach (string il in sehirler)
            {   
                lstOgeler.Items.Add(il);
            }

Ekran Çıktısı

Yukarıdaki örnekte sehirler dizisindeki her bir eleman için dön ve her bir elemanı ListBox ‘ın item ‘larına (elemanlarına) ekle demiş olduk. Koleksiyonun her bir elemanı string olduğu için tipini en başta belirmiş olduk.

Yukarıdaki "il" adlı string değişkene "foreach iterator" denir. Bu yapı döngü nesnesi yardımıyla her bir elemanı tek tek ele alabilmemizi sağlar. Ancak bu elemana herhangi bir şekilde değer ataması yapmanız mümkün değildir.

Foreach Döngüsü ile koleksiyon içerisindeki her bir elemana değer atma :

Foreach döngüsünde sadece ileriye doğru bir iteration (Forward Only) söz konusudur. Ayrıca içerisindeki iteratioin variable (öteleme değişkeni) Read Only yani sadece okunabilirdir. Bu nedenle değer atarken bu değişkene değil, bu değişkenin temsil ettiği elemana değer atanmalıdır.

Öncelikle hatalı kullanıma bir örnek verelim. Yine “Sehirler” adında, boyutu 7 olan bir string dizi tanımlayalım.
string[] sehirler = new string[7];

Bu dizinin tüm elemanlarına “adana” değerini atamaya çalışalım.

            foreach (string il in sehirler)
            {
                il = "Ankara";
            }

Yukarıda da bahsettiğimiz gibi derleme zamanındaki (Compile Time) hata mesajında da görüldüğü gibi (Cannot assign to 'il' because it is a 'foreach iteration variable) ”iteration değişkenine değer atanamaz.” hatasını almaktayız. Bunun yerine örneğimizde  koleksiyon olarak dizi kullandığımız için o dizinin elemanlarına değer atamalıyız. Bunun için kaynakDizi adında kaynak bir dizi tanımlayalım. Bu dizinin elemanlarını da hedefDizi adındaki hedef dizimizin elemanlarına atalım. İndex bilgisi için de index adında integer tipli bir değişken tanımlayalım ve her döngü adımında bu index değişkenin değerini, ilgili dizinin sıradaki elemanına geçmesi için bir arttıralım.

           string[] kaynakDizi = { "Adana", "Adıyaman", "Afyon", "Ağrı", "Amasya", "Ankara", "Antalya" };
            string[] hedefDizi = new string[7];
            int index = 0;
            foreach (string il in sehirler)
            {
                //il = "Ankara";
                hedefDizi[index] = kaynakDizi[index];
                lstOgeler.Items.Add(hedefDizi[index]);
                index++;
            }

Fakat bu örnek için for döngüsü daha idealdir. Foreach döngüsü genellikle tanımladığımız sınıf  (class) tiplerini koleksiyon şeklinde tutabilen tiplerin elemanlarında dönmek için kullanılır. Örneğin “urun” adında bir class tanımlayalım ve ad, stok, fiyat gibi bir takım özelliklere sahip olsun. Ve herhangi bir ürün nesnesi “get” edilmek (okunmak) istendiğinde “full-qualification name” (namespace.class) ismi çıkmaması için object sınıfındaki ToString() metodunu farklı davrandıralım yani aşağıdaki gibi “override” edelim.

    public class urun
    {
        public string ad { get; set; }
        public int stok { get; set; }
        public decimal fiyat { get; set; }

        public override string ToString()
        {
            return this.ad;
        }
    }

Her bir elemanı bu sınıf tipinde olan bir koleksiyon tanımlamak için “List<>” generic sınıfından faydalanabiliriz. Bu sınıfın “Add” metedu sayesinde koleksiyona kolay bir şekilde eleman ekleyebiliriz, tabi ki şart tipi ürün olması gerekiyor.

     List<urun> urunler = new List<urun>(); 

           
            urunler.Add(new urun { ad = "Domates", fiyat = 2.5m, stok = 100 });
            urunler.Add(new urun { ad = "Biber", fiyat = 1.5m, stok = 250 });
            urunler.Add(new urun { ad = "Patlıcan", fiyat = 2.0m, stok = 50 });

Artık elimizdeki bu koleksiyonun elemanlarında foreach kullanarak dönebiliriz, değerlerini okuyaabilir herhangi bir kontrole yazdırabiliriz.

     foreach(urun u in urunler)
            {
                listBox1.Items.Add(u);
            }

“urunler” koleksiyonun içerisindeki her bir elemanın tipi “urun” olduğundan dolayı tip belirtildi. Artık bu elemanlara foreach içerisinde “u” adıyla seslenilecektir. Herhangi bir ListBox kontolüne eklediğimizde urun sınıfındaki “ToString” metodu çalışarak ürünün adı görünecektir.


Buradaki asıl dikkat edilmesi gereken nokta foreach döngüsüni kullanarak herhangi bir koleksiyonda dönebilmek için o koleksiyonun .Net ortamında “IEnumerable” interface ‘ini implamente almış olması gerekir. Çünkü bu interface içinde “GetEnumarator” adında bir metod bulunur ki bu metod da IEnumerator interface ‘ini implemante almıştır. IEnmarator interface ‘i içerisinde ilgili sınıf nesnesinin foreach döngüsünde döenebilmesini sağlayan 3 adet metod bulunur: Current, MoveNext, Reset olmak üzere. Current foreach içerisinde o anki elemanı teslim eder, MoveNext sıradaki elemana geçmek için koleksiyonun index ‘ini 1 arttırır. Sırada eleman varsa true sonucu döner ve o eleman get edilmek istendiğinde yine Current metodu çalışacaktır. Sırada eleman yoksa da false dönecektir ve index değerini -1 yapan yani döngüden çıkılmasını sağlayan Reset metodunu çağıracaktır. Custom yani geliştirici tanımlı normal ya da generic koleksiyon tanımlamasını ilerleyen yazılarımda anlatacağım. Örneğin List tipine sağ tıklayıp “go to defination” seçeneğini seçtiğimizde “IEnumarable” interface ‘ini implemante almış olduğunu görüyoruz.




Ya da c sharp ‘ta “ComboBox.ObjectCollection” ve ya “ListBox.ObjectCollection” gibi IEnumerable interface ‘ini implamente almış (uygulamış) olan özel koleksiyon tiplerinin elemanlarında dönebilmek için de kullanılabilir. Hemen bir örnek üzerinde görelim:
    
     foreach (urun u in listBox1.Items)
            {
                if (u.ad == "Domates")
                {
                    MessageBox.Show("5 kilo al!");
                }
            }

ListBox ‘ın item ‘larının da .Net ‘deki tanımlamasına gittiğimizde IEnumerable interface ‘ini implamente almış olduğunu görürüz.




Diğer bir örnek, formunuzdaki tüm TextBox ‘ların Text özelliklerini temizlemek isteyebilirsiniz. Bunun için teker teker TextBox ‘ların textini temizleyeceğinize aşağıdaki gibi foreach döngüsünü kullanarak, formun kontrollerinde gezip, eğer bu kontrol TextBox ise textini temizle diyebilirsiniz. Tabi ki bir tip kontrolü yapılırken “is” anahtar kelimesiyle yapılmalıdır. Ayrıca "Controls" koleksiyonu bize formun üzerindeki tüm kontrolleri Control tipinde teslim edecektir. Fakat dikkat edilmesi gereken nokta TextBox ‘ın Text özelliğine erişebilmek için elde ettiğimiz kontrolü TextBox ‘a “cast” etmeliyiz ki; o kontrol TextBox gibi davranabilsin. Böyle TextBox nesnesinin “Clear” metodu sayesinde TextBox ‘in texti temizlenebilir.

     foreach (Control ctrl in this.Controls)
            {
                if (ctrl is TextBox)
                {
                    (ctrl as TextBox).Clear();
                }
            }

This” anahtar sözcüğü kullanıldığı sınıfın nesnesini temsil eder. (Bu yüzden static sınıflarda “this” sözcüğü kullanılmaz.) Bu örnekte “this” sözcüğünü, Form1 sınıfı içerisinde kullandığımız için, bu sınıfın bir öğesi gibi davranacaktır. “Controls” koleksiyonunun .Net ortamındaki tanımına gittiğimizde yine IEnumerable interface ‘ini implamente almış olduğunu görürüz.


IEnumerable ve IEnumarator interface kullanımı daha sonraki makalelerimde ayrıntılı bir şekilde ele alacağım. Başka bir yazıda görüşmek dileğiyle, hoşçakalın... 

Hiç yorum yok:

Yorum Gönder