Aklımda Kalası Kelimeler

* давайте работать вместе
* Zarf ve Mazruf, Zerafet(xHoyratlık) ile aynı kökten(za-ra-fe) gelir
* Bedesten
* Suç subuta ermiştir - Suç sabit olmuştur

1 Mart 2010 Pazartesi

C# Event

Delegate ile ilgili makalemi okuyarak buradan okumaya devam ederseniz dünya daha güzel bir yer olabilir ;)

Event meselesini futboldan örnekle anlatmaya çalışayım.
Örneğin bir FutbolOyuncusu sınıfını düşün. Sahaya bu sınıftan 22 tane nesne yaratarak süreceksin. Her oyuncunun özellikleri, yeteneği, hızı, gücü farklı olacak.
- Golcü nesnesi(mesela Mario Gomez :) pozisyona girecek ve şut atmayı düşünecek ve keyfi bir hızla topu tepecek.
- Şut atıldığında bizim bir olayı tetiklememiz gerekiyor ki, diğer oyuncular buna göre pozisyon alsınlar.
- Ancak bu olay ayrıca Gomez'in o anda kaç km hızla topa vurduğu, açısını, zamanlamasını OnShoot olayına geçirmesini gerektirebilir.
- Olayın parametrelerine göre(hız, zaman, açı vs.) göre kaleci şutu görebilecek, topa uzanabilecek, tutacak ya da tokatlayacak.
- Olayın peşine, diğer nesneler(kaleci, rakip savunma, hücumcu oyuncular vs.) bir cevap üretebilmek için bu olayı takip ediyor olabilirler(bu olaya abone olabilirler).
- Ya da Mario atamaz diye hiç bir eylem içinde bulunmayabilir diğerleri.

* İşte OnShoot bizim EVENT'imiz(olayımız) anlaştık mı?
* OnShoot olayı anında bazı parametrelerimiz açığa çıkıyor (hız, açı, zamanlama vs.) bunlarda olay argumanları(Event Arguments).

Kod yazalım ve içindeki yorumlarımıza bakarak ilerleyelim:
using System;
using System.Data;
using System.Runtime;

namespace CSharp_Orneklerim.Delegate_Event
{
    public class Event_01
    {
        public static void Calis()
        {
            Topcu kaleciMuslera = new Topcu() { Adi = "Muslera" };
            Topcu cenkGonen = new Topcu() { Adi = "Cenk Gönen" };

            // Olaylara abone olacak nesnelerin fonksiyonlarını ekleyelim
            cenkGonen.OnShoot += (sutuVuranOyuncu, sutunOzelligi) =>
            {
                // Muslera hiçbir topu tutamasın ve top ağlarla buluşsun.
                if (!kaleciMuslera.Yakalamayi_Dene(sutunOzelligi))
                {
                    // TopunDurumu türünden dönecek sonucu 
                    // hakem gol olarak ilan edecek
                    Console.WriteLine("Muslera > - Ah Vah ah vah...");
                    return new TopunDurumu()
                           {
                               x1 = -2,
                               x2 = -2,
                               y1 = -2,
                               y2 = -2,
                               Hizi = 0
                           };
                }
                else
                {
                    // kaleci topu tekmeyle sahanın ortasına uzaklaştırsın
                    return new TopunDurumu()
                    {
                        x1 = 0,
                        y1 = 12,
                        x2 = 20,
                        y2 = 15,
                        Hizi = 20
                    };
                }
            };

            // herkes pozisyonunu aldı ve golcümüz ceza sahasına giriyor
            cenkGonen.Ceza_Sahasina_Topla_Gir();
        }
    }

    public partial class Topcu
    {
        /*
         * Çeşitli özellikleri olabilir
         * tackle : topu ayağından almak
         * speed : hız
         * size : boyut
         */
        public string Adi;

        /// <summary>
        /// Tüm oyuncular ceza sahasına girebilir(savunma, hücum vs.)
        /// Ancak davranışları elbette farklıdır. 
        /// Örneğin rakip hücumcu girerse kaleyi görüp şut çekerken,
        /// kendi ceza sahasına giren savunmacı uzaklaştırmak isteyecektir.
        /// Bu yüzden bu metodu virtual yapalım ki, Topcu sınıfını miras alacak başka sınıflar
        /// kendi isteklerine göre ezebilsinler.
        /// </summary>
        public virtual void Ceza_Sahasina_Topla_Gir()
        {
            /** 
             * Oyuncu topu ceza sahasına sürsün ve kaleyi görsün.
             * Şut çekecek ve bizim olayımız tetiklenecek
             * this oyuncumuz şutu çekti
             */
            Shoot shoot = new Shoot()
                          {
                              Aci = 60, // Topa 60 dereceyle 
                              Hiz = 50, // 50 km hızda 
                              Zaman = DateTime.Now, // şimdi vursun
                          };

            TopunDurumu durumu = veShoooot(shoot);
            // Şut atıldığında bir olayı(event) tetiklemek istiyoruz. 
            // Böylece bu olayı dinleyen tüm abonelere şut ile ilgili bilgi verebilelim
            // Görünüşe göre veShoooot metodu bu işi yapacak.
            // veShoooot metodu Shoot türünden bir değişken alsın ve topun durumunu dönsün
            // Bu metodu birazdan tanımlayacağız 
            // Ancak önce oluşturacağımız metodun dönen tipi ve parametrelerine bakalım
            //
            // 1 Parametre geçireceğiz bu metoda Shoot tipinde bir değişken
            // ve topun son durumu dönecek: {koordinatı, yönü, hızı} 
            //
            // Demekki Shoot ve TopunDurumu diye iki sınıfımızı yazmalıyız
        }
    }

    // 1. Shoot sınıfı
    public partial class Shoot
    {
        public int Aci, Hiz;
        public DateTime Zaman;
    }

    // 2. Topun durumu
    public class TopunDurumu
    {
        // iki nokta bir vektör tanımlar. konumu ve Yönünü bulduk.
        public int x1, y1, x2, y2, Hizi;
    }

    // 3. OnShoot olayı için method handler
    // OnShoot olayı(eventi) için bir delegate tipinde metot tutucu tipine ihtiyacımız var
    public delegate TopunDurumu Del_Donussuz_Shoot_Parametreli(Topcu _topcu, Shoot _shoot);

    // 4. Topcu sinifina OnShoot olayi tanımlama vakti geldi
    // OnShoot eventini Topcu sınıfına eklemeliyiz ki, tetikleyebilelim
    public partial class Topcu
    {
        // event tanımlamaları sınıf içinde yapılır ve ancak sınıftan tetiklenebilirler
        // event değikenlerine doğrudan bir atama(eventDegiskeni = null) yapılamaz
        // ancak += ya da -= ile atama yapılabilir
        public event Del_Donussuz_Shoot_Parametreli OnShoot;
        
        // Geldik OnShoot olayımızı tetikleme işini yapacak metodumuza.
        // Metodumuz event tetikleyecek. Event ancak sınıfın içinden tetiklenebilir, nesneden değil!
        // bu yüzden public erişim belirleyicisini kullanamayız.
        // Topcu sınıfından türetilen tiplerin de bu metodu çağırması gerekecek 
        protected virtual TopunDurumu veShoooot(Shoot shoot)
        {
            // OnShoot bizim tetikleyeceğimiz olayımız olacak(event)
            // OnShoot olayının tanımlandığı yerde varsayılan bir değer yok! Null kontrolü yapalım ! 
            if (OnShoot == null)
            {
                throw new NoNullAllowedException("OnShoot olayına hiç metot eklenmiş olmaz, olamaz!");
            } 
            return OnShoot(this, shoot);
        }

        /// <summary>
        /// Shoot parametresi ve kalecinin özellikleri arasında bir fonksiyonun sonucuna göre
        /// gol ise true, değilse false dönebilir. Ama burada kaleci Muslera ise kesin gol diyelim
        /// </summary>
        /// <param name="_shoot"></param>
        /// <returns></returns>
        public bool Yakalamayi_Dene(Shoot _shoot)
        {
            if (this.Adi == "Muslera")
            {
                return false; // tutamadı, gol
            }
            return true;
        }
    }

    /**
     * ----- YAPIYI ANLAYALIM:
     * Farkettiyseniz olayı tetiklendiğinde, bu olaya abonelere
     *   olayı kimin yaptığı,
     *   nasıl bir parametreyle gerçekleştiğini dönüyor
     *   Sonuçtada abonelerin topa müdahele edeceği düşünüldüğü için TopunDurumu
     *   tipinde bir sonuç değerini bekliyoruz.
     * 
     *           TopunDurumu OnShoot(Topcu sender, Shoot e) 
     *       
     * 
     * ----- GENEL DELEGATE TİPİ TANIMLAMAYA ÇALIŞALIM:
     * Bu OLAY TANIMINI genelleyerek şu metodu olay tutucu metodun imzası olarak elde edebiliriz:
     * 
     *             void On_BirŞey_Oldu(object sender, OlayParams e)
     *             
     *        
     * ----- GENEL OLAY PARAMETRE TİPİ TANIMLAMAYA ÇALIŞALIM
     * Bu metot tutucuyu şöyle yazabiliriz:
     * 
     *        delegate void OnDelegate(object sender, EventArgs e)
     *       
     * Bu tanımala gereği bizim EventArgs diye "genel olay parametreleri" tutan bir üst sınıfa ihtiyacımız var
     * Bu sınıftan kendi parametrelerimizi taşıyan sınıflar yaratarak aynı delegate tipini kullanabiliriz
     *  
     * ----- EVENTHANDLER TİPİ İŞİMİZİ GÖRECEK SANKİ
     * Yukarıdaki delegate tip tanımına uyan genel bir tip var adı: EventHandler tipi. 
     * Hem her olaya uygun olan EventArgs tipinde parametresi de var
     * 
     *      public delegate void EventHandler(object sender, System.EventArgs e)
     *      
     * Şeklinde tanımlı ve yukarıdaki imzaya uygun(void dönen ve iki parametre alan[object, EventArgs] metotlar için)
     * EventHandler tipini kullanabiliriz artık.
     */

    // ----- GENEL OLAY PARAMETRE TİPİ olan EventArgs'dan türesin olay parametre tipimiz
    public partial class Shoot : EventArgs { }

    /** 
     * Artık olay tanımımızı değiştirebiliriz
     */

    public partial class Topcu
    {
        // Başlangıç için boş bir isimsiz metodu değer olarak atadık ki 
        // eventi tetikleyeceğimiz yerde if(OnShoot2 != null){ OnShoot2(..) } null kontrolüne gerek kalmasın
        public event EventHandler OnShoot2 = delegate(object sender, EventArgs args) { };
    }
    /**
     * OnShoot2 metoduna parametre olacak gelen args değerleri Shoot türünden olabilir.
     * Ancak içeride bilinçli(explicit) cast(tür dönüşümü) etmeliyiz
     * 
     * kaleciMuslera.OnShoot2 += (sutuVuranOyuncu, sutunOzelligi) => Console.WriteLine(((Shoot)sutunOzelligi).Hiz);
     * 
     * gibi. 
     * Yada generic tip olan EventHandler<T> ile EventArgs türünden değil, kendi parametre türümüz olan Shoot türünden
     * bir method handler yaratmasını söylemeliyiz. 
     * 
     * Aşağıdaki gibi:
     */

    public partial class Topcu
    {
        // Generic tipli EventHandler'ı kullanarak bizim arguman tipimizden değerleri geçiriyor olağız.
        // eventi tetikleyeceğimiz yerde if(OnShoot3 != null){ OnShoot3(..) } null kontrolüne gerek kalmasın
        public event EventHandler<Shoot> OnShoot3 = delegate(object sender, Shoot args) { };
    }

    /**
     * Eğer Topcu sinifindan türeyen bir SolBek tipimiz olsa ve onun da OnShoot olayını tetiklemesi gerekse
     */

    public partial class SolBek : Topcu
    {
        public override void Ceza_Sahasina_Topla_Gir()
        {
            base.Ceza_Sahasina_Topla_Gir();
        }

        /// <summary>
        /// Sol beklerin şutu azıcık farklı olsun. 
        /// Mesela sol ayağına gelirse normal golcüden 2 kaplan daha güçlü vursun ;)
        /// </summary>
        public void SolAyaklaShoot()
        {
            // Topu düzeltsin,
            // Sol ayağına alsın, 
            // hede höde yapsın ve vursun. OnShoot olayı ata sınıftan tetikleneceği için
            // base.veShoooot metodunu çağırsın
            base.veShoooot(new Shoot()
                           {
                               Hiz = 100,
                               Aci = 70, //azicik yamuk vursun
                               Zaman = DateTime.Now
                           });

            // sonra ellerini başının arasına alsın,
            // Geri koşsun vs. vs....
        }
    }
}


Şimdi biz neden event diye bir
Çalışan hali
using System;
using System.Threading;

namespace caEvent1
{
public delegate void OnDemlendi();

class CayMakinasi
{
public CayMakinasi()
{
Console.WriteLine("Çay koyuldu...");
}

public event OnDemlendi eventDemlendi;

public void Demle()
{
Console.WriteLine();
Console.Write("Çay demleniyor ");
for (int i = 3; i > 0; i--)
{
Console.Write(".");
Thread.Sleep(1000);   
}
Console.WriteLine();
Demlendi();
}

private void Demlendi()
{
if (eventDemlendi!=null)
{
Console.WriteLine("  ---       DEMLİK İÇİNDE İŞLEMLER YAPILIYOR       --- ");
Console.WriteLine("  --- Demlikte işlem yapılıyor. Bitince haber verilecek");
Console.WriteLine("  --- ");
Console.Write("  --- Demleniyor");
for (int i = 3; i > 0; i--)
{
Thread.Sleep(3000);
Console.Write(".");
}
Console.WriteLine("  --- ");
Console.WriteLine("  --- Demlikten Haber VAR: <Çay demlendi>");
Console.WriteLine();
Console.WriteLine();
eventDemlendi();
}
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Çay yapmaya başlıyoruz...");
Console.WriteLine();

CayMakinasi cm = new CayMakinasi();
cm.eventDemlendi += new OnDemlendi(cm_OnDemlendi);

cm.Demle();

Console.WriteLine();
Console.WriteLine("Tavşan kanı bunlaaarrr...");
}

static void cm_OnDemlendi()
{
Console.WriteLine("**************************************************************");
Console.WriteLine("* Çayın olduğunda, makine metot işaretçisini tetikler.");
Console.WriteLine("* ");
Console.WriteLine("* O işaretçiyede kendi metodumuzu iliştiririzki ");
Console.WriteLine("* tam o sıra bizde işlemler yapabilelim.");
Console.WriteLine("* ");
Console.WriteLine("* Buna en güzel örnek, düğmeye tıklandığında işlem yapmamız");
Console.WriteLine("*  ya da GridView'a her satır eklendiğinde (RowDataBound) ");
Console.WriteLine("* satıra müdahale etme isteğimiz verilebilir.");
Console.WriteLine("**************************************************************");

}
}
}




Çalışan hali
using System;
using System.Threading;

namespace caEvent1
{
public delegate void OnDemlendi();

class CayMakinasi
{
public CayMakinasi()
{
Console.WriteLine("Çay koyuldu...");
}

public event OnDemlendi eventDemlendi;

public void Demle()
{
Console.WriteLine();
Console.Write("Çay demleniyor ");
for (int i = 3; i > 0; i--)
{
Console.Write(".");
Thread.Sleep(1000);   
}
Console.WriteLine();
Demlendi();
}

private void Demlendi()
{
if (eventDemlendi!=null)
{
Console.WriteLine();
Console.WriteLine("  *** Demlikten Haber VAR: <Çay demlendi>");
Console.WriteLine();
Console.WriteLine();
eventDemlendi();
}
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Kahvehanede müşteri seslenir.");
Console.WriteLine("  - Garsooon 3 çaaay...!");
Thread.Sleep(1000);
Console.WriteLine("Garon cevap verir.");
Console.WriteLine("  - Taze yapıyorum Ali abicim...");

Thread.Sleep(1500);            
Console.WriteLine("Çay Makinesinin fişi takılır(nesne yaratılır),");
CayMakinasi cm = new CayMakinasi();

Thread.Sleep(4000);
Console.WriteLine("Çay olunca makine ötecek. Bizde bu sinyali duyar duymaz çay almaya gideceğiz.");
cm.eventDemlendi += new OnDemlendi(cm_OnDemlendi);

Thread.Sleep(2000);
Console.WriteLine("Çay Makinesinin ÇAY YAP düğmesine basılır(nesnenin metodu tetiklenir)");
cm.Demle();

Console.WriteLine("Tavşan kanı bunlaaarrr...");
}

static void cm_OnDemlendi()
{
Console.WriteLine("Garson makinenin başına gelir ve 3 çay koyar.");
Thread.Sleep(2500);
}
}
}

Peki birde metin okuyan ve "okuma tamamlandığında" olayında çalışan fonksiyon örneğiyle bir kod görelim:
using System;
using System.Speech.Synthesis;
using System.Threading;

namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            SpeechSynthesizer synthesizer = new SpeechSynthesizer();
            synthesizer.Volume = 100;  // 0...100
            synthesizer.Rate = -2;     // -10...10

            // Synchronous
            synthesizer.Speak("Merhaba Cem");
            synthesizer.SpeakCompleted += (a, b) =>
                                          {
                                              Console.WriteLine("Konuşma tamamlandı");
                                          };

            // Asynchronous
            var p = synthesizer.SpeakAsync("Asynchronous talk with Speak Async");
            while (!p.IsCompleted)
            {
                Console.WriteLine("Async function is still continuing");
                Thread.Sleep(100);
            }
            Console.WriteLine("End of Main Thread");
        }
    }
}