Delegeler(Delegate) ve Olaylar(Event)

DELEGELER

Nedir?

Delegelerin ne olduğunu tarif etmeden önce nerede kullanıldıklarına bakalım ilk olarak.

Delegelerin 4 temel kullanım yeri/amacı var.

  • Olayları tanımlarken
  • Runtime sırasında hangi metodun çağrılacağını, hangi nesneye başvuracağımızı bilmiyorsak. Mesela dinamik eklenen kontrolleriniz vardır ve o sırada kullanıcı hangisine tıklarsa onla ilgili işlem yapılacaktır. Veya Random metodu ile rastgele bazı operasyonlar yapılıyordur, buralarda kullanılabilir.
  • Callback metodlarında.
  • Bir metodu başka metodlara parametre olarak gönderirken

Peki nedir bu delege denen şey? Delegeleri, metodların yer tutucusu olarak düşünebilirsiniz. Delege gördüğünüz her yere, o delegenin tanımlanmasında kullanılan imzaya(dönüş tipi ve parametreler) uygun her metod gelebilir demek. Yani delegeler, adı üzerinde, bu imzaya sahip tüm metodları temsil ederler.

Kullanımı

Delege kullanımının 3 aşaması vardır

  1. Delege tanımlanır(declaration)
  2. Delege yaratılır(Instantiation)
  3. Delege çağrılır (Invocation). Bu da 2 şekilde olur.
    • a)Metodun kendisi çağrılır. Bu da kendi içinde 2 şekilde olabilir
      • doğrudan bir metod gibi çağırma. Ör:myDel()
      • invoke metodu ile çağırma(özel durumlar dışında çok kullanılmaz)
    • b)metod parametre olarak gönderilir

1.aşama:Deklarasyon(Tanımlama)

Delegeler namespace seviyesinde de class seviyesinde de tanımlabilirler.

delegate dönüştipi delegenin_adı(parametreler)

//Örnek
namespace delegeOrnek {
delegate string myDelege1(string str);//namespace seviyesinde. tüm classlarda kullanılabilir. dönüş tipi de parametresi de string olan metodları temsil eden bir delege
public class Test {
delegate void myDelege2(string str);//class seviyesinde. dönüş tipi void parametresi ise string olan metodları temsil eden bir delege
}
}

2.aşama: Yaratma(Instantiation)

//1.yöntem
delegeadı delegenesnesininadı=new delegeadı(metodadı) //metod adından sonra parantez yok
Ör:
delİslem myDel= new delİslem(Topla);

//2.yöntem. c#2.0 ile birlikte bu işlem daha da basitleşti, doğrudan atama yapıyoruz
delİslem myDel=Topla;

//3.yöntem:anonymous metod ile tanımlama, detayı aşağıdaki örnekte
//4.yöntem:lambda deyimelrle tanımlama, detayı aşağıdaki örnekte

3.aşama:Kullanım

//1.kullanım şekli:metodun çağrılması
int sonuc = myDel(5, 4); //doğrudan çağırma. Aynen temsil ettiği metod gibi kullanılır
int sonuc = myDel.Invoke(5, 4); //Invoke metoudu ile çağırma

//2.kullanım şekli: parametre olarak gönderim
BaşkaBirMetod("falan filan", Topla)

public static int BaşkaBirMetod(string str, delİslem mydel) {
//detaylı örnek aşağıda olacak
}

Tabi diyeceksiniz ki, bu yukardaki örnek çok saçma, delegeyle melegeyle ne uğraşıyoruz ki, direkt metodu çağıralım gitsin. Evet haklısınız, bu örneği sadece kullanımı göstermek için basit tuttum. Delegelerin esas güzelliği, böyle yukarıdaki gibi metodların tek tek atanması ve çağrılmasında değil, onların aynı anda birkaç metodu üzerlerinde taşıyor olabilmelerinde saklı. İşte böyle çoklu metod ataması durumuna multicasting denir. Bunun detaylarını az sonra göreceğiz.

Şimdi bir de delegelerin başka bir açıdan kullanımlarının incelenmesine gelelim. Delegeler, aynı sınıf içindeki metodlar için de kullanılabilir, başka sınıftakiler için de. Bunun için ilgili sınıfının instancenın alınması yeterlidir. Keza delegeler hem statik metotlar hem de instance metodları için kullanılabilirler. Aşağıda c#indepth sitesinden aldığım çeşitli örnekler var.

// The following two creation expressions are equivalent,
// where InstanceMethod is an instance method in the class
// containing the creation expression (or a base class).
// The target is "this".
FirstDelegate d1 = new FirstDelegate(InstanceMethod);
FirstDelegate d2 = new FirstDelegate(this.InstanceMethod);

// Here we create a delegate instance referring to the same method
// as the first two examples, but with a different target.
FirstDelegate d3 = new FirstDelegate(anotherInstance.InstanceMethod);

// This delegate instance uses an instance method in a different class,
// specifying the target to call the method on
FirstDelegate d4 = new FirstDelegate(instanceOfOtherClass.OtherInstanceMethod);

// This delegate instance uses a static method in ths class containing
// the creation expression (or a base class).
FirstDelegate d5 = new FirstDelegate(StaticMethod);

// This delegate instance uses a static method in a different class
FirstDelegate d6 = new FirstDelegate(OtherClass.OtherStaticMethod);

 

Şimdi de, şuana kadar anlatılanların tümünü bir arada göreceğimiz bir örneğe bakalım. Bu örnekte üç tür delege olacak, biri string işlemleri, bir diğeri int işlemleri, sonuncusu da callback fonksiyon çağrısı yapacak.(Eventlere ayrıca bakacağız)

//Program.cs dosyasının içeriği

using System;
//1.Aşama:sınıf üstünde deklarasyon, assembly içindeki tüm sınıflarda kullanılabilir.

delegate string strMod(string str);//dönüş tipi de parametresi de string olan metodları temsil eden bir delege
delegate int delİslem(int x, int y);//dönüş tipi int ve int tipinde iki parametresi olan metodları temsil eden bir delege

class DelegateTest
{
 static string ReplaceSpaces(string a)
 {
 Console.WriteLine("Boşluklar tire ile değişti");
 return a.Replace(' ','-');
 }

 static string RemoveSpaces(string a)
 {
 string temp = "";
 int i;
 Console.WriteLine("Boşluklar silindi");
 for (i = 0; i < a.Length; i++)
 if (a[i] != ' ')
 temp += a[i];
 return temp;
 }

 static string MakeZero(string a)
 {
 Console.WriteLine("Boşluklar 0 yapıldı");
 return a.Replace(' ', '0');
 }

public static void Main()
 {
 //2.aşama:delege instantiation'ı yani yaratımı.
 strMod strOp = new strMod(ReplaceSpaces);

//3.aşama:Invoke ediyoruz, yani delege aracılığıyla ilgili metotlari çağırıyoruz.
 string str = strOp("Bu bir denemedir");

 Console.WriteLine("Sonuç: " + str);
 Console.WriteLine();

str = strOp.Invoke("Bu da invoke metodoyla çağrı");
 Console.WriteLine("Sonuç: " + str);
 Console.WriteLine();

strOp = new strMod(RemoveSpaces);//başka bir metodu temsil etsin istedik
 str = strOp("Bu bir denemedir");
 Console.WriteLine("Sonuç: " + str);
 Console.WriteLine();

strOp = new strMod(MakeZero);//şimdi de başka bir metodu temsil etsin istedik
 str = strOp("Bu bir denemedir");
 Console.WriteLine("Sonuç: " + str);
 Console.WriteLine();
 Console.ReadLine();

//Şimdi farklı bir class olan işlemler classındn bir obje üretilip onda aritmetik işlemler başlayacak
 Console.WriteLine("İşlemler objesi yaratılıyor...");
 İslemler isl = new İslemler();
 isl.hesapla();
 strMod delSta = İslemler.Statikmetod;
 Console.WriteLine("Statik metod çağırlıyor");
 Console.WriteLine("Sonuç: " + delSta("volkan"));
 Console.WriteLine();
 Console.ReadLine();

//şimdi de farklı bir class olan Myclass classından bir obje üretilip callback örneği yapacağız. Bu ayrıcai metodun paramtere olarak gönderilmesi örneğidir
 Console.WriteLine("Callback örneği başlıyor...");
 Console.ReadLine();
 Myclass myc = new Myclass();
 Console.WriteLine("Önce callbacksiz versiyonu");
 Console.ReadLine();
 myc.UzunÇalışanAmaCallBacksiz();
 Console.ReadLine();
 Console.WriteLine("Şimdi callbackli versiyonu");
 Console.ReadLine();
 myc.UzunÇalışanMetod(CallBackMetodu);
 //burada bir yerde i'nin değerini elde edebiliriz. farklı bir konu olduğu için burada detaya girmeyeceğim
 Console.ReadLine();
 }

static void CallBackMetodu(int i)
 {
 Console.WriteLine(i);
 }
 }

//****************İslem.cs içeriği**********************

using System;
class İslemler
{
 static int mystatic = 0;
 public static string Statikmetod(string s)
 {
 return "merhaba" + s;
 }
 public int Topla(int a,int b)
 {
 return a + b;
 }

public int Carp(int a, int b)
 {
 return a * b;
 }

public void hesapla()
 {
 //2.aşama:delege yaratıyoruz(instantaion), 1.aşama yani deklerasyon, DelegateTest classını da üstünde namespace seviyesinde yapılmıştı
 delİslem myDel = new delİslem(Topla);

 // 3.aşama:delegeyi invoke ediyoruz
 int sonuc = myDel(5, 4); // sonuc 9. Topla yerine myDel koymuşuz gibi, delegenin görevi bu, Topla metdounu temsil ediyor.
 Console.WriteLine("5 ve 4 için Topla işleminin sonuc:" + sonuc);
 myDel = new delİslem(Carp);
 sonuc = myDel(5, 4); // sonuc 20
 Console.WriteLine("5 ve 4 için Çarpma işleminin sonuc:" + sonuc);

//düz atama yöntem
 delİslem mydel2;
 mydel2 = Topla; //new falan demeden doğrudan atama, veya tek satıda "delİslem mydel2=Topla". bu yöntem c#2.0 ile geldi.
 Console.WriteLine("8 ve 2 için düz atama ile topla işleminin sonuc:" + mydel2(8,2));
 mydel2 = Carp; //şaun Carp'ı temsil ediyor
 Console.WriteLine("8 ve 2 için düz atama ile çarp işleminin sonuc:" + mydel2(8, 2));

//+= yöntemi(multicasting. özellikle çoklu metodu çalıtşıracaksan)
 delİslem mydel3= new delİslem(Carp);
 mydel3 += Topla; //new falan demeden doğrudan atama
 Console.WriteLine("8 ve 2 için multicasting(+=) yöntemi ile topla işleminin sonuc:" + mydel3(8, 2)); //anlamsız oldu ama çalııştı. önce 8*2=16 yapar, bunu bi yerde kulanmaz, sonra 8+2=10 yapar, bunu gösterir. Multicasting kullanımını ayrca göreceğiz

//anonymous metod ile tanımlama, c# 2.0 ve sonrası
 delİslem mydel4 = delegate (int a, int b) { return (a + b); }; //anonymouslarda gövde içinde parametre yoksa delegateten sornaki () içinde de gerek yok
 Console.WriteLine("3 ve 4 için anonymous ile toplam :" + mydel4(3, 4));

//lambda exp ile tanımlama, c# 3.0 ve sonrası
 delİslem mydel5 = (a, b) =&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; a * b;
 Console.WriteLine("5 ve 2 için lambda ile çarpım: "+ mydel5(5, 2));

//Func ile. func&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;input1,input2,output&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;, bu da .NET 3,5 ile geldi. func kullandığımızda delege tanımlamaya gerek kalmamaktadır. son paremteresi sonuç, öncekiler inputtur. tek paramter varsa, bu input anlmaındadır, sonucu voiddir. Funcları lambdalı da yazabiliyoruz lamvdasız da, lambdlı daha kısadır tabi.
 Func&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;int, int, int&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; Toplam = (a, b) =&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; a + b;
 Console.WriteLine("5 ve 8 için Func ile Toplam:" + Toplam(5,8));
 Console.ReadLine();

//metodarı parametre olarak gönderme örneği
 Console.WriteLine("Metodlar paramtere olarak gidecek");
 Console.WriteLine("");
 Console.WriteLine("metodu paremtre olarak göndermenin toplam soncu 505 olmalı:" + ozelyontem("volki", Topla)); //Topla yerine new delİslem(Topla) da yazılabilirdi, ama c#2.0 ile simple way yöntemini kullandım. volki kelimesi 5 harfli, özelyönteme gidiyor, 5le 100 çarpılıyor ve 500 hesaplanır, sonra Topla metdu devreye girer ve 5le 500ü toplar, 505 yapar.
 Console.WriteLine("metodu paremtre olarak göndermenin toplam soncu 5000 olmalı:" + ozelyontem("volki", Carp));//static değişken hazfızdadır, 500dür, sonra 500+5*100=1000 olur, Carp metodu devereye girer, 5*1000=5000 olur.
 }

public static int ozelyontem(string str, delİslem mydel)
 {
 mystatic = mystatic + str.Length * 100;
 return mydel(str.Length, mystatic);
 }
}

//************Myclass.cs içeriği*************
using System;
public class Myclass
{
 //sınıf içi deklerasyon örneği, sadece bu sınıfta kullanılabilir
 public delegate void CallBackDelegesi(int i);
 public void UzunÇalışanMetod(CallBackDelegesi del) //bu metod, içine bu delegate imzasını taşıyan bir metdou parametre olarak alıyor
 {
 //bunda geçen rakamlar(yani i), Main içinde yakalanabiliyor, belli bi an i'nin değeri alınabilir, async kullanım lazım tabi.
 for (int i = 0; i < 10000; i++)
 {
 del(i);
 }
 }

public void UzunÇalışanAmaCallBacksiz()
 {
 //bunda geçen rakamlara müdahalemiz yok, yakalayamıyoruz, rakamalr akıp geçiyor
 for (int i = 0; i < 10000; i++)
 {
 Console.WriteLine(i);
 }
 }
}

 

Multicasting

Bazen bir delegeye birden çok metod bağlamak isteriz. Bunu özellikle event kullanımında yaparız. (ama illa event olmak zorunda değil.) Bu delegeyi invoke ettiğimizde bu metodlarının hepsinin çalışmasını isteriz.

Bunun amacı da invoke ettiğimiz metodların, çağırdığımız yerin kapsamı içinde bulunmasının anlamsız oluşudur.  Şöyle düşünelim, uzunca çalışan bir metodunuz var, birbiriyle ilişkili birsürü iş yaptı,  sonra birbirinden bağımsız 10 farklı iş daha yapacak, bu ana metodun içine bu 10 işi yapan metodları tek tek eklemenin bi anlamı yok, gereği de yok. Örneği somutlaştıralım. Evde bir robotunuz var, onu programlıyorsunuz, ana işi “ev işlerini” yapmak diyelim, en son da diyeceksiniz ki, “kalan işleri hallet”. Uzun metodun içine çeşitli evtoplama görevlerini yaptırdınız, kalan bisürü ıvır zıvır işin metodunu ana iş metodu içine yazmazsınız, onun yerine en sonda “kalanişlerihallet” delegesini çağırırsınız. Bu delegeye de birbiriyle alakasız işleri atarsınız.(Eventleri henüz bilmediğinizi farzediyorum, aslında bu senaryo tam da event kullanmalık bir senaryo, ama biz event yerine delege kullanmayı tercih edeceğiz)

İşte bu çoklu metod bağlama işi, += işareti ile veya Delegate sınıfını Combine statik metodu ile yapılır. Genelde += işareti kullanılır, bu işaret zaten arka planda Combine’a dönüştürülür. Keza metodun ilişkisini kesmek için de -= işareti veya Remove statik metodu kullanılır.

 

//Program.cs'de Main metodununu sonuna şunları ekleriz

 Console.WriteLine("Multicasting başlıyor...");
 Console.WriteLine("");
 Robot r = new Robot();
 r.İslerihallet();
 Console.ReadLine();

//Robot.cs içeriği
using System;

delegate void kalanisleriyap(); //parametresiz void metodları temsil eden bir delege
public class Robot
{

 public void İslerihallet()
 {
 //...
 //evi topamakla ilgili çeşitli komutlar. yereri sil, toz al, evi süpür, çamaşır yıka v.s
 //......
 Console.WriteLine("ev toplandı");
 Digerisler();
 }

public void Digerisler()
 {
 kalanisleriyap mydel = null;
 mydel += Faturalarıöde;
 mydel += Eveyemeksiparişver;
 mydel += Falanfilanyap;
 mydel.Invoke();//metod gibi çağırmak için Invoke metodu kullanılır. Direkt mydel() de yazılabilir
 }

public void Faturalarıöde()
 {
 Console.WriteLine("faturalar ödendi");
 }

public void Eveyemeksiparişver()
 {
 Console.WriteLine("Yemek siparişi verildi");

}

public void Falanfilanyap()
 {
 Console.WriteLine("falan filan yapıldı");

}

Bu örnek de yine anlamsız gelmiş olabilir, ama sadece nasıl kullanıldığını göstermek için bu kadar basit oldu, bu multicastingin esas kullanımını Olaylar içinde göreceğiz.

OLAYLAR

Eventler, tanımlama şekli olarak fieldlara benzerler ama delege tipinde tanımlanırlar. Unutulmaması gereken birşey var ki; Eventler, delege isntance’ları değildir, tamamen ayrı birer memberdır.

Eventler tetiklenen nesnelerdir, bir diğer deyişle trigger edilirler. Bunlar bir mouse tıklaması olabileceği gibi bizim yarattığımız olaylar da olabilir.

Eventlerle ilgili 3 temel terim , 6 da aşama vardır. Şimdi bunlara bakalım.

Terminoloji

  1. Event: Eventin kendisi. Ör:Click
  2. Event tetikleyeci metod, publisher da denir: Geleneksel tanımlama şekli: OnEventadı. Eventin raise olmasını, yani meydana gelmesini sağlar. Ör:OnClick
  3. Event handler metodu, subscriber da denir. Event gerçekleştiğinde çalışacak metoddur.  Bunlar bir tane olabileceği gibi genelde birden çok olurlar. Kendilerini temsil eden delegeleri gibi void tipindedirler ve az sonra detayını göreceğimiz iki parametre alırlar. Ör:button1_Click

Aşamalar

  1. Eventi temsil edecek delegenin tanımlanması(Çoğu durumda zorunlu değil. Zira C#2.0 ile birlikte gelen EventHandler delegesi event tanımlama için yeterli olmaktadır. İlla kendi delegenizi tanımlamanız gerekirse eventadıEventHandler convention’ına göre tanımlanır. Eventler voiddirler çünkü genelde bunlara multicasting uygulanır. Multicasting uygulanan delegeler de void olmak zorundadır.
  2. İlgili delege tipinde Event tanımlanır.
  3. Event, Event handler metoduna bağlanır (subscribe edilir)
  4. Eventin trigger metodu tanımlanır
  5. Event invoke/raise edilir(Bir olayın involke edilmesi sadece tanımlandığı sınıf içinden gerçekleşir.)
  6. Event handler metodu çalışır

Bütün bunları öncelikle yerleşik bir event olan Click eventi ve bu eventin sahibi olan bir buton nesnesi üzerinde inceleyelim, sonra kendi yarattığımız eventler üzerinde görelim.

Önemli not: Delegeler namespace seviyesinde tanımlanabilirken eventler mutlaka bir class içinde tanımlanmalıdır.

//1.aşama: eventi taşıyacak olan delegenin tanımı. C#'ta hazır gelir
public delegate void EventHandler(object sender, EventArgs args)

//2.aşama: eventin tanımı. C#'ta hazır gelir
//public event delegeAdı EventAdı
public event EventHandler Click

//3.aşama: eventhandler metoduna bağlama. Bunu biz Form üzerine yerleştirdiğimiz bir butona çift tıkladığımızda c# otomatik olarak bizim adımıza halleder. Form1.Designer.cs dosyasının içine bakarsanız görürsünüz
//EventAdı += new EventHandler(metod adı)
this.button1.Click += new System.EventHandler(this.button1_Click);

//4.aşama:Trigger metodu tanımlanır. C#'ta hazır gelir
//protected virtual void OnEventAdı şeklide tanımlanması tavsiye edilir
//protected virtual void OnClick
//5.aşama:Event raise edilir, yani oluşturulur
//mouse ile düğmeye tıklanınca arka planda OnClick metodu çalışır.

//6.aşama:Evente bağlanan eventhandler metotları çalışır
//button1_Click metodu

Şimdi, bütün bunları kendi eventlerimizde nasıl yapıyoruz ona bakalım. Her ne kadar mouse ile düğmeye tıklama çok somut ve anlaşılır olsa da, aşağıdaki örnekten göreceksiniz, bizim örneğimiz daha anlaşılır olacak, zira arka planda çalışan OnClick gibi bir subscriber metod yerine  kanlı canlı çalışan, elle dokunabildiğimiz bir subscriber metodumuz olacak.

Kendi eventlerimizi yaratalım

Senaryomuz şöyle: Bi listbox var, bu listboxa rasgele sayılar ekleyen bir buton var. Listboxa 10. kayıt eklendikten sonra, listedolu adındaki eventimiz trigger olacak ve bu evente bağlanmış olan 2 metod çalışacak.

Şimdi soru şu? Liste dolduğunda olayı trigger edeceğimize neden direkt bu iki metodu çalıştırmıyoruz. Cevap:Listenin dolduğunu kontrol ettiğimiz metodun diğer 3 metodla organik bir bağı yoktur. Bu sorunun cevabı birçok yerde şöyle anlatılır; bu metodumuzun diğer iki metodun ne yaptığını bilmesi gerekmiyor denir. Bu bana tatminkar bir açıklama olarak gelmiyor, bilmesin ne farkeder, yine de event yaratmayı çok açıklamıyor ki. Bence işin özü, diğer iki metodun ne yaptığın bilip bilmemesi değil, o ik metodun bu metodun içeriği ile mantıksal bir ilişki içinde olup olmaması.

Şimdi, gelelim kod kısmına;

//1.aşama: eventi taşıyacak olan delgenin tanımı. Yine hazır olan EventHandler delegesini kullanabiliriz

//2.aşama: eventin tanımı.
public event EventHandler Listbecomefull //ingilizce kullanmaya çalışıyorum, copy paste kod değidlir 🙂

//3.aşama: eventhandler metoduna bağlama. Bunu constructor içine bağlıyorum, ki form yratılır ayratılmaz bağlama işi olsun
public Form1()
 {
 InitializeComponent();
 this.Listbecomefull += new EventHandler(Listedoluhaberiver);
 //veya kısaca Listbecomefull += listedoluhaberiver;
 //kısa formu kullanmak daha iyi, çünkü eventhandler delege tipini değiştirdiğinde her yerde değiştirmen gerekir,
 this.Listbecomefull += MakeLabel2VisibelAndRed; //bu da 2.eventhandler

}

//4.aşama:Trigger metodu tanımlanır.
protected virtual void OnListbecomefull(object source, EventArgs e) //veya parametresiz, veya sadece eventargs. bunda zorunluluk yok, bu iki parematernin zorunlu olduğu yer bu metod değil eventhandler metodu
 {
 if (Listbecomefull != null)
 {
 Listbecomefull(this, e); //tanımlarken parametresiz olsaydı buraya eventargs.empty
 }

//veya kısaa Listbecomefull?.Invoke(this, e);
 }

//5.aşama:Event raise edilir, yani oluşturulur
//button8, yukarda bahsettiğim, listboxa yeni rakam ekleyen düğmedir.
private void button8_Click(object sender, EventArgs e)
 {
 var r = new Random();
 if (listBox1.Items.Count < 10)
 {
 listBox1.Items.Add(r.Next(1, 100).ToString());
 if (listBox1.Items.Count == 10)
 {
 MessageBox.Show("liste doldu, başka kayıt ekleyemezsiniz");
 OnListbecomefull(this, EventArgs.Empty);//event raise ediliyor. bu raise'i alan tüm metodlar çalışmaya başlar

 }
 }
 else
 {
 MessageBox.Show("liste şuan dolu, daha kayıt ekleyemezsiniz");
 }

}

//6.aşama:Evente bağlanan eventhandler metotları çalışır
public void Listedoluhaberiver(object sender, EventArgs e)
 {
 MessageBox.Show("event trigger oldu, liste doludur");
 }

public void MakeLabel2VisibelAndRed(object source, EventArgs e)
 {
 label2.Visible = true;
 label2.ForeColor = Color.Red;
 label2.Text = "liste dolduuuuuuuu";
 }

(Şimdi bu yukarıdaki örnekteki eventhandlerlar button8 ile çok da alakasız değil gibi görünüyor, yine aralarında bir ilişki var gibi görünüyor ama tamamen alaksız bir örnek yapmak da açıkçası zor.)

NOT:Delegelerde olduğu gibi eventlerde de kolay yazım yöntemi uygulanabilir. Yani;myobj.EventX += new myDel(button_click) yerine kısaca myobj.EventX += button_click yazılabilir.

Detaylar

Event tanımında 2 aşamanın birleşimi

Bazı sitelerde veya kitaplarda, hatalı bir yönlendirmeyle, event tanımı yapılırken event ile trigger metodun birleştirildiği görülür. Ben gerek microsoftun kendisni gerek başka sitelerde gördüğüm yöntemi, yani bunların ayrı ayrı belirtildiği yöntemi benimsedim. Ancak başka yerde görürseniz şaşırmayın, kafanız karışmasın diye de bunu söyleme ihtiyacı duydum. Mesela biz yukarda Listbecomefull diye bir event ve OnListbecomefull diye subscriber metod tanımladık. Bu dediğim sitelerde OnListbecomefull direkt eventin adı olabiliyor.

Peki biz(aslında Microsoft) neden bu ikisini birbirinden ayırma metodunu benimsemiş? Cevap, trigger metodun imzasında saklı. Bu metod, protected virtual olarak tanımlanır. Yani bu eventin tanımlandığı sınıfı inherit ettiğiniz bu metodu kullanabilirsiniz, çünkü protected’dır. Normalde ne demiştik, bi eventi sadece bulunduğu sınıf içinden invoke edersiniz, işte protected tanımlanması onu derived edilmiş sınıflarda da kullanmanıza olanak verir. Bu metodu override da edebilirsiniz çünkü virtualdır. Böylece bu event olduğunda ilave işler de yaptırabilirsiniz. Mesela TextBoxı kalıtan bir sınıf yarattığınızda bunun OnClik eventine tüm metni seçtiren bir kod yazabilirsiniz, ki microsoftun kendi sitesinde verdiği örnek budur.

public class SingleClickTextBox: TextBox
{
	protected override void OnClick(EventArgs e)
	{
		this.SelectAll();
		base.OnClick(e);
	}
}

Event delegeleri ve EventHandler delegesi

Buraya kadar anlatılanlardan delegelerin rolünü anlayabildiniz mi? Delegeler ne yapıyordu? Bir metodun yerini tutuyordu, hatta birden çok metodu atayarak delegeye bu metodları taşımasını söylüyorduk. Event yaratırken de onları delege tipiyle yaratmamızın altında da bu gerekçe yatıyor. Eventlere metod bağlamak. Bu event olduğunda şu şu metodları taşı (ve aslında çalıştır) demiş oluyoruz.

.Net çatısı bize, event tanımlarken kullandığımız delegelerin void tipinde ve iki parametresi olması gerektiğini söyler. İlki, eventin kaynağıdır. İkincisi de ilave bilgi geçirilmesini sağlayacak olan EventArgs’tır.

Bu ikinci paremetreyi kullanmayacak eventler için .Net içinde hazır gelen EventHandler delegesi kullanılır bunu yukardaki örneklerde kullanmıştık, ki bunun ilave datayı destekleyen versiyonu da var, o da EventHandler<TEventArgs> delegesidir. Bu ikisinin de kulanılmayacağı yerlerde yine void ve iki parametreli olmak koşuluyla kendi delegelerimizi yaratabiliriz.

Gerek kendi tanımladığımız delegeler olsun gerekse EventHandler<TEventArgs> delegesi olsun, bunlara ikinci parametre için yeni bir sınıf tanımlarız, ve bu sınıfı EventArgs sınfından türetiriz.

Kendi delegenizi yaratmak istediğinizde takip edilecek bir isimlendirme conventionı vardır: EventadıEventHandler. Ör:listeDolduEventHandler

Eventhandlerın parametreleri

İlk parametre, olayın referansıdır dedik, ki bu genelde gönderen sınıf olur ve this şeklinde aktarılır. İkincisi, varsa diğer bilgileri içerir. Böyle bir bilgi yoksa bunlar için EventArgs.Empty değeri geçilebilir. “Ben Eventargsla falan uğraşmak istemiyorum” diyorsanız,  EventHandler delegesini kullanmaz ve kendi parametresiz delegenizi de yaratabilirsiniz ama bu çok tavsiye edilmez ve Microsoftun önerdiği conventionlara sadık kalmanız önerilir.

Konuyu basit tutmak adına EventArgs’ın kullanıldığı senaryoları buraya almıyorum. Zaten Delege ve Event konuları, hazmetmesi biraz zor konular. Bunları hazmettikten sonra Eventargs konusu çıtır çerez kalıyor, bununla ilgili bilgileri birçok yerde sade ve anlaşılır bir şekilde bulabilirsiniz.

Birçok eventi tek bir event handler metoduna bağlamak

Bazen olur ki, birden çok düğmenin(veya başka kontrolün) tıklanması durumunda hep aynı metodun çalışmasını istersiniz. Böyle bir durumda, desigerner.cs dosyasında tüm eventlere aynı event bağlanabileeği gibi, properties ekranından ilgili controlün click eventine uygun eventhanler metod seçilerek de yapılabilir. Sonrasında da ilgili metod için de sender’ın hangisi olduğuna bakılarak işlem yapılabilir.

Button btn= (Button)sender;
if (btn.Name == "Button1")
{
.....
}
else
{
.....
}

Bu işlem delege ve lambda/anoymus kullanrak da ypılabilir. Bununla ilgili başka bir örneği bundan sonra ele almayı düşündüğüm lambda/anonymus postumda bulabilirsiniz

Click Eventi

Bir düğmenin Click Eventi var demiştik. Ve biz somut bir şekilde bu düğmeye tıkladığımızda OnClick tirgger metodu çalışarak düğmenin Click eventini harekete geçirir ve bunun soncunda da button1_click gibi bir event handler metodu dediğimiz metod çalışarak istenilen tepkiyi verir.

Peki somut bir tıklama yerine bu düğmeye programatik bir şekilde tıklayabilir miyiz? Evet. Bunun için 3 yöntemimiz var, ki üçü de OnClick metodunu çağırır.

  • button1.PerfromClick() (c#4.0dan itibaren)
  • button1_Click(this, new EventArgs()); veya this yerine null, veya button1_Click(this,null); veya button1_Click(null,null);
  • InvokeOnClick(button1, EventArgs.Empty)

Anonymous ve Lambda ile kullanım

Tek bir olay çalışacaksa ve kısaysa anonymous içinde yazılabilir.

button1.Click +=(sender,args) => {
//sadece s ve e de olur???
MessageBox.Show("Olay oldu");
Close();//formu
}

veya hiç argüman ve sender yoksa direkt delegate ile
button1.Click += delegate {
MessageBox.Show("Olay oldu");
Close();//formu
}

Detaylar için bir sonraki post’a bakın.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s