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"); } } }