Yazılım Mimarileri ve Tasarım Desenleri Üzerine

C# Delegate(Temsilci) ve Event(Olay) Kullanımı

Bu yazımda C#’ta Delegate(delege) yani temsilci ve Event yani olay konularından bahsedeceğim.Tabi şunu belirtmek istiyorum ki, yazımı sayfalara bölerek adım adım anlatmayı daha verimli buluyorum.Delegeler interface gibi tek başına bir anlamlı konu değildir.Kafanızda bu konuyu nerede kullanacağım gibisinden sorular olabilir.Ama Event konusuyla bağdaştırınca konuyu daha iyi irdeleyip anlamaya başlayacaksınız.
Delegate
Delegeler, metodların adreslerini dolayısıyla metodların kendilerini tutabilen, işaret edebilen yapılardır.
NOT : Metodlar da bellekte oluşan ve bellek adresleri olan yapılardır.
Delegeler referans tipli yapılardır.Yani nesne oluşturulabilirler.
Şimdi bir delege yapısı nasıl olur bakalım.
[Erişim Belirleyicisi] delege [geri dönüş tipi] [delege ismi] (eğer varsa parametre)
Örnek olarak aşağıdaki delege yapısını inceleyiniz.

public delegate void MyDelegeHandler();
//Geri dönüş tipi olmayan ve parametre almayan metodlar için kullanılır.

Delege temsil edeceği metoda imzası benzemek zorundadır.Yani yukarıdaki delegeye geri dönüş tipi int olan bir metod ya da parametre alan başka bir metod veremeyiz. Şunuda izah edeyim ki, delege isimlerinin sonuna Handler konmak yazılım geleneğinin adetlerindendir.Zorunlu değildir ama bu dünyanın kültürlerindendir. 🙂 Aşağıdaki delege türlerini inceleyeniz.

public delegate void MyDelegeHandler2(int a,int b);
//Geri dönüş tipi olmayan ve int tipinden iki adet parametre alan metodlar için kullanılır.
----
public delegate int MyDelegeHandler3(int a,int b);
//Geri dönüş tipi int olan ve int tipinden iki adet parametre alan metodlar için kullanılır.
----
public delegate string MyDelegeHandler4();
//Geri dönüş tipi string olan ve parametre almayan metodlar için kullanılır.

Buraya kadar delegenin yapısıyla ilgili bilgi verdim.Şimdi delegeyi kullanmayı görelim.

        public delegate void CalismaHandler();
        public void x()
        {
            MessageBox.Show("x metodu");
        }

Yukarıda, bir tane geriye değer göndermeyen ve parametre almayan delegate ve bir tane geriye değer göndermeyen parametre almayan metodumuz mevcuttur.

        private void Form1_Load(object sender, EventArgs e)
        {
            x();
        }

İstesek x metodunu yukarıdaki gibi çağırıp çalıştırabiliriz. Peki bu x metodu CalismaHandler delegate i tarafından nasıl çalıştırılabilir? Delegate dediğimiz yapı bir referans tipli yapı olduğundan referansını tanımlamalıyız.Ve bu referansı tanımladıktan sonra o delegeden bir nesne yaratıp istediğimiz metodu çalıştırmasını sağlayabiliriz.

        private void Form1_Load(object sender, EventArgs e)
        {
            CalismaHandler delege = new CalismaHandler(x);
//Benim yukarıda oluşturduğum delegeden nesne yaratıyorum.
        }

Yukarıda “delege” isimli delege nesnesine , x metodunu çalıştıracaksın demiş olduk. Şimdi ise , “delege” isimli delege nesnesine , x metodunu çalıştıralım.

        private void Form1_Load(object sender, EventArgs e)
        {
            CalismaHandler delege = new CalismaHandler(x);
            delege();//delege isimli delegate nesnesi üzerinden x referansını çalıştırmış olduk.
        }

Yukarıda formun loadında yazılan bu kod derlenip çalıştırıldığında mesaj kutusunda “x metodu” yazacaktır. Aynı şekilde aşağıdaki gibi bir çalıştırmada yapabiliriz.

        private void Form1_Load(object sender, EventArgs e)
        {
            CalismaHandler delege = new CalismaHandler(x);
            delege.Invoke();//delegenin işaret ettiği metod, delegenin Invoke metodu aracılığıyla da çalıştırılabilir.
        }

Şimdi ise delege kullanımına bir örnek daha gösterelim.

        public delegate int IslemYapHandler(int a,int b);
        public int Topla(int s1, int s2)
        {
            return s1 + s2;
        }
        public int Carp(int s1, int s2)
        {
            return s1 * s2;
        }

Yukarıda gördüğünüz gibi,geriye int tipinden değer gönderen ve int tipinden iki parametre alan bir tane delege,geriye int tipinden değer gönderen ve int tipinden iki parametre alan iki metod yazdım.

        private void Form1_Load(object sender, EventArgs e)
        {
            IslemYapHandler delege = new IslemYapHandler(Topla);
            MessageBox.Show(delege(2, 3).ToString());
            //veya
            MessageBox.Show(delege.Invoke(2, 3).ToString());
        }

Ve yazdığım bu metodları “delege” isminde delegate nesnesine adreslettirdim.delege(2,3) veya delege.Invoke(2,3) diyerek, programı derleyip çalıştırdığımda ekranda 5 yazısını göreceğim. NOT:”delege” isimli delegate nesnesi hem Topla hem de Carp metodlarını adresleyebilir.Ben yukarıda sadece Topla metodunu adreslettim. Şimdi gelin Carp metodunuda “delege” isimli delegatemize adresleyelim.

delege += Carp;

“delege” isimli delegate ye Topla metodu verilmişti.Şimdi aynı delegate ye Carp metodu da adreslettiriliyor.Yani bir delege aynı anda birden fazla metodu adresleyebilir.Gördüğünüz gibi bunu ” += ” operatörüyle yapıyor.” += ” operatörü ile metod eklenirken, ” -= ” operatörüyle metod çıkartılabilir.( += in anlamı bağladır.) Şimdi eklenmiş halde bu programı çalıştıralım.Tabi kodlarımız böyle dağınık olunca anlamakta zorlanırsınız.Onun için bir kısmı yeniden derleyip yazıyorum.

            IslemYapHandler delege = new IslemYapHandler(Topla);
            delege += Carp;
            MessageBox.Show(delege(2,3).ToString());
            //ya da
            MessageBox.Show(delege.Invoke(2,3).ToString());

Eğer yukarıdaki kod bloğunu derleyip çalıştırırsanız ne olur? Sanıyorsunuz ki, delegemizin içinde iki metod var ve ikiside çalıştırılacak.Yani önce Topla metodu çalıştırılıp 5 sonucunu verecek, ardından Carp metodu çalıştırılıp 6 sonucu verilecek.Yada buna benzer durumlar olacak.. Ama sandığınız gibi değil. 🙂 (bende başta bu şekilde sanmıştım.) Sonuç olarak 6 cevabı verilecektir.Çünkü delegeye bağlanan en son metod çalışır.Burada delegemize bağlanan en son metod Carp metodumuz olduğu için, o çalışacaktır.Yani Topla metodu çalışıp, ardından Carp çalışmayacaktır.

            IslemYapHandler delege = new IslemYapHandler(Topla);
            delege += Carp;
            delege -= Carp;
            MessageBox.Show(delege(2,3).ToString());

Yukarıdaki kod bloğunu incelerseniz eğer, delegemize önce Topla metodu ekleniyor.Sonra Carp metodu ekleniyor.En son olarak Carp metodu yeniden cıkarılıyor.Eee haliyle en son eklenen metod çalışıyorsa , en son çıkarılan metoddan bir önceki çalışmak zorundadır.Yani burada Topla metodu çalışacaktır. Bunu şöyle genelleyebilirim.A,B,C metodlarını sırasıyla delegemize bağlıyoruz.Bu durumda çalıştırdığımızda C metodu çalışacaktır.C yi çıkardığımız da, B metodu çalışacaktır.Son olarak B cıkarsa A metodu çalışacaktır.


Şimdide delegemiz içindeki tüm metodlarımız da sırasıyla gezebilmek için,delegate referansımız üzerinden GetInvocationList() metodunu kullanıyoruz.

        private void Form1_Load(object sender, EventArgs e)
        {
            IslemYapHandler delege = new IslemYapHandler(Topla);
            delege += Carp;
            Delegate[] metodlarımız = delege.GetInvocationList();
            foreach (Delegate item in metodlarımız)
            {
                MessageBox.Show("Metodumuzun adı : " + item.Method.Name);
                MessageBox.Show("Metodumuzun geri dönüş tipi : "+item.Method.ReturnType);
                int sonuc = (int)item.DynamicInvoke(2, 3);
                MessageBox.Show("Şuanki metodun sonucu : " + sonuc.ToString());
            }
        }

Yukarıda gördüğünüz gibi delegemizin içinde Topla ve Carp metodları vardır.Delegate’imizin referansına GetInvocationList() metodunu çalıştırdığımızda,geriye Delegate[] dizisi tipinden veriler dönüyor ve ben bunları Delegate[] metodlarımız dizisine alıyorum.Foreach döngüsüyle bu dizide dolasıyorum.Bu dizideki metodların özelliklerine(adı,geridönüş değeri v.s.) bakabiliyorum.Burada değinmem gereken bir metod var.DynamicInvoke(),bu metod sayesinde o anda item referansına gelen metodu çalıştırabiliyoruz.Params tipinden değişken aldığı için metodların çalıştıracağı kadar parametre yazıyorum.

Bu sayfada Delegate leri görmüş olduk.Bir sonraki sayfada Event yapısından ve delegatlerle birlikte kullanımından bahsedeceğim.
Event
Tanım olarak çok ayrıntıya girmek istemediğim bir konudur.Bir buton nesnesine farenin sol tuşuyla tıkladığımızda,bir textbox nesnesine bir karakter girdiğimizde ya da fareyle combobax daki elemanlardan birini seçtiğimizde bir olay gerçekleşir.İşte bu durumların hepsi bir olaydır.
Şimdi formumuza bir Button nesnesi koyalım ve Click Eventına aşağıdaki şekilde ulaşalım.

        private void Form1_Load(object sender, EventArgs e)
        {
            button1.Click += new EventHandler(Tiklandi);
        }
        void Tiklandi(object sender, EventArgs e)
        {

        }

Click olayı Control sınıfı içinde tanımlanmış bir Event tır.O sınıf içerisinde aynı zamanda Click olayının meydana gelip gelmediği de sürekli kontrol edilmektedir.Eğer o sınıf, Click olayının oluştuğunu tespit ederse , ” += ” ile o olaya bağladığım metod çalıştırılacaktır.Halbuki o sınıf benim Click e bağladığım bu sınıftaki metottan haberdar olmayacaktır.O sınıfı, bu sınıftaki metottan haberdar etmek için,bu sınıftaki metodu çalıştırabilmek için yine o sınıfta tanımlı olan delegeyi(EventHandler) kullanıyoruz.

Burada anlatmak istediğim pek aydınlatıcı ve hatta anlaşılır olmamış olabilir.Bir sonraki sayfada asıl mevzuya giriyorum.Bir Classtaki Event a, başka bir Classtaki metodu bağlayabilecek olan delegeyi oluşturacağım.

Sizlere bu sayfada bic classtaki eventa,başka bir classtaki metodu bağlamayı göstereceğim.

    class SayiKontrol
    {
        public delegate void SayiKontrolEtHandler();
        public event SayiKontrolEtHandler SayiDurumu;
        int sayi;
        public int Sayi
        {
            get
            {
                return sayi;
            }
            set
            {
                sayi = value;
                if (sayi<10)
                {
                    // Olay çalışsın
                    // Ey olay, sana SayiKontrolEtHandler delegesinin bağladığı metodu çalıştır diyeceğiz :)
                }
            }
        }
    }

Yukarıda gördüğünüz SayiKontrol sınıfında,geri dönüş tipi olmayan,parametre almayan metodlara uygun SayiKontrolEtHandler adında bir delege yazılmıştır.Ve bu delege tipinde,SayiDurumu adında bir event yazılmıştır.
UNUTMAYIN!!! : Eventların tipi,kendisine metod bağlayacak delegenin tipinden olur.

    class SayiKontrol
    {
        public delegate void SayiKontrolEtHandler();
        public event SayiKontrolEtHandler SayiDurumu;
        int sayi;
        public int Sayi
        {
            get
            {
                return sayi;
            }
            set
            {
                sayi = value;
                if (sayi < 10)
                {
                    if (SayiDurumu != null)
                    {
                        SayiDurumu();
                    }
                }
            }
        }
    }

Eğer Sayi propertysine 10 dan küçük bir sayı girilirse SayiDurumu eventı fırlatılacaktır.Şimdi SayiDurumu eventına metod bağlayalım.

        private void Form1_Load(object sender, EventArgs e)
        {
            SayiKontrol nesne = new SayiKontrol();
            nesne.SayiDurumu += new SayiKontrol.SayiKontrolEtHandler(kontrol);
            nesne.Sayi = 77;
        }
        void kontrol()
        {
            MessageBox.Show("Sayi özelliği 10 dan küçük olamaz");
        }

Gördüğünüz gibi, SayiDurumu eventına ” += ” operatörü vasıtasıyla SayiKontrolEtHandler delegesi üzerinden kontrol metodunu bağladım.Eğer bu programı çalıştırırsanız kontrol metodu çalıştırılmayacaktır.Çünkü,SayiDurumu eventına SayiKontrolEtHandler delegesi üzerinden kontrol metod bağlanıyor ve Sayi özelliğine 77 rakamı set edildiğinde,classtaki sayi fieldına gelen değerin 10 dan küçük olmadığını bildiği için if scopeları içine girmeden akışı durduruyor.Yani SayiDurumu eventı şu durumdayken atılmıyor.Eğer 10 dan küçük değer girersek kontrol metodu çalışacaktır.

Bir sonraki yazımda görüşmek üzere..
İyi çalışmalar.

Exit mobile version