C Sharp Programlama Dili/Olaylar

Ders 25. Olaylar


Olaylar geçen derste öğrendiğimiz temsilcilerle yakından ilişkilidir. Geri dönüş tipi temsilci olan özelliklere olay diyebiliriz. İşlevsel açıdan olayların geri dönüş tipi temsilci olan özelliklerden bir farkı yoktur. Ancak olay tanımlamak için ilgili özellik tanımına event anahtar sözcüğü eklenir. Olaylar bir üye elemandır ve sınıflar, yapılar ve arayüzler içinde tanımlanabilir. Olayın arayüz içinde tanımlanması durumunda bu arayüzü uygulayan sınıf ve yapıların bu olayı içermesi gerekir. Olaylar virtual, override, sealed override ve abstract olarak belirtilebilir. abstract olarak belirtilmesi durumunda bu sınıfı türeten sınıf veya yapının bu olayı uygulaması gerekir. Olayların geri dönüş tipi sadece bir temsilci olabileceği için olaylar belirli bir şablonda bir veya daha fazla metodu temsil edebilir. Olay bildirim şablonu şöyledir:

erişim_belirleyici event temsilci olay_ismi;

Bu şablona göre bir olay bildirimi şöyle olur:

using System;
delegate void Temsilci();
class Sinif
{
    public event Temsilci Olay;
    public void Metot()
    {
        Olay();
    }
}
class AnaProgram
{
    static void Main()
    {
        Sinif s = new Sinif();
        s.Olay += new Temsilci(Metot);
        s.Metot();
    }
    static void Metot()
    {
        Console.WriteLine("Deneme");
    }
}

Bu program ekrana Deneme yazacaktır. Bir nesnenin bir olayına += ve -= operatörleri kullanılarak metot eklenir ve çıkarılır, sade atama operatörü kullanılamaz. Bu olay tetiklendiğinde bu eklenen metot(lar) çalıştırılır. Olayın tetiklenmesi olayın tanımlandığı sınıf içinde yapılır, başka sınıflarda yapılamaz. Olayın tetiklenmesi metot çağırır gibi yapılır. Bu örneğimizde olayın tetiklenmesi başka bir sınıf içinden yapılamayacağı için sınıftaki bir public metot çağrılmış, bu metot olayı tetiklemiştir. Olay gerçekleştiğinde çalıştırılacak metot ya da metotların statik olma zorunluluğu yoktur.

Herhangi bir metot bağlanmamış bir olayı tetiklemeye çalışırsanız çalışma zamanı hatası oluşur. O yüzden çoğunlukla olayı tetikleyen metodun içinde olaya herhangi bir metot bağlanıp bağlanmadığı aşağıdaki gibi kontrol edilir.

public void Metot()
{
    if(Olay!=null)
        Olay();
}

Burada öncelikle olaya bir metot bağlanıp bağlanmadığı kontrol edilmektedir. Olaya metot bağlanmışsa olay tetiklenmekte, olaya metot bağlanmamışsa tetiklenmemektedir.

Bir önceki derste gördüğümüz metot isimlerinin temsilci türüne bilinçsiz dönüşebilmesi sayesinde bir olaya sadece metot ismini yazarak da bağlama yapabilirsiniz. Örnek:

s.Olay += Metot;

Olayların kalıtımdaki rolü değiştir

Eğer olayın içinde bulunduğu sınıf türetilirse ve yavru sınıf olaya isim gizleme yapmaz veya override etmezse olayın erişim belirleyicisi public olsa bile yavru sınıf olayı tetikleme hakkını kaybeder. Bu sorunun önüne geçmek için ana sınıfta erişim belirleyicisi en az protected olan ve tek görevi olaya metot bağlanmışsa olayı tetiklemek olan bir metot oluşturulabilir.

add ve remove erişimcileri değiştir

Bazen bir olaya += operatörü ile metot eklendiğinde veya -= operatörü ile metot çıkarıldığında belirli kod bloklarının çalıştırılmasını isteyebiliriz. İşte bunun için ilgili olayın blokları içine add ve remove blokları koyulur. add ve remove bloklarını sahte özelliklerdeki set ve get bloklarına benzetebiliriz.

Örnek program:
 using System;
 delegate void Temsilci();
 class deneme
 {
    event Temsilci Olay
    {
       add
       {
          Console.WriteLine("Olaya metot eklendi.");
       }
       remove
       {
          Console.WriteLine("Olaydan metot çıkarıldı.");
       }
    }
    static void Main()
    {
       deneme d=new deneme();
       d.Olay+=new Temsilci(d.Metot1);
       d.Olay+=new Temsilci(d.Metot2);
       d.Olay-=new Temsilci(d.Metot1);
    }
    void Metot1(){}
    void Metot2(){}
 }

Bu programın ekran çıktısı şöyle olur:

Olaya metot eklendi.
Olaya metot eklendi.
Olaydan metot çıkarıldı.

Bir olayın add ve remove erişimcileri olması durumunda bildiğimiz yollarla olay tetiklenemez. Bu sorunu çözmek için sınıfta geri dönüş tipi bir temsilci olan bir özellik tanımlanır. add erişimcisi ile verilen temsilci nesnesi bu özelliğe eklenir, remove erişimcisi ile verilen temsilcisi nesnesi -eğer varsa- bu özellikten çıkarılır. Daha sonra sınıf içinde olayın tetiklenmesini istediğimiz durumlarda olayın kendisi değil, bu özellik tetiklenir. Örnek:

using System;
delegate void Temsilci();
class Sinif
{
    private Temsilci OlayOzellik;
    public event Temsilci Olay
    {
        add
        {
            OlayOzellik = (Temsilci)Delegate.Combine(OlayOzellik, value);
        }
        remove
        {
            OlayOzellik = (Temsilci)Delegate.Remove(OlayOzellik, value);
        }
    }
    static void Main()
    {
        Sinif s = new Sinif();
        s.Olay += Metot1;
        s.Olay += Metot2;
        s.OlayOzellik();
        s.Olay -= Metot2;
        s.OlayOzellik();
    }
    static void Metot1()
    {
        Console.WriteLine("Metot1");
    }
    static void Metot2()
    {
        Console.WriteLine("Metot2");
    }
}

Bu programın ekran çıktısı aşağıdaki gibi olacaktır:

Metot1
Metot2
Metot1

Delegate sınıfına ait statik Remove() metodu bir temsilcinin içinden başka bir temsilciye ait metotları çıkarır. Bu metotların çıkarılabilmesi için metotları çıkarılacak temsilci nesnesindeki metotların aynı sırada ardışık olarak kaynak nesnede bulunması gerekir. Çıkarılacak temsilci nesnesindeki metotlar birden fazla kez aynı sırada ve ardışık olarak kaynak nesnede varsa sadece sonuncusu çıkarılır.

NOTLAR:

  1. add ve remove bloklarını içeren olaylar özet (abstract) olarak bildirilemez.
  2. Bir olayda add ve remove blokları birlikte olmalıdır. Ayrı ayrı olamazlar.
  3. Aynı metot += operatörüyle birden fazla kez bir olaya bağlanabilir. Bu durumda olay gerçekleşmesi durumunda metot birden fazla kez çalışacaktır.
  4. Olaya bağlanmamış bir metot -= operatörüyle çıkarılmaya çalışılırsa herhangi bir hata oluşmaz.
  5. add ve remove erişimcileri alan bir olayı "sahte olay" olarak düşünebiliriz. Çünkü add ve remove erişimcileri alan bir olay için bellekte herhangi bir temsilci nesnesi depolanmaz. Sadece ilgili sahte olaya += operatörüyle bir temsilci nesnesi bağlanmaya çalışıldığında add bloğunun içindeki, -= operatörüyle bir temsilci nesnesi çıkarılmaya çalışıldığında remove bloğunun içindeki kodlar çalıştırılır. Bunun dışında bir şey yapılmaz. O yüzden bir sahte olay tanımı yaptığımızda mutlaka aynı sınıfa geri dönüş tipi aynı temsilci (veya temsilci dizisi) olan bir özellik eklememiz gerekir.

Windows uygulamalarında olaylar değiştir

İleride göreceğimiz Windows uygulamaları geliştirirken olaylarla iç içe yaşayacağız. Forma bir buton getirdiğimizde aslında Button sınıfı türünden bir nesne yaratırız. Button sınıfının Click olayı vardır. İşte biz form tasarlama penceresinde (ileride göreceğiz) herhangi bir butona çift tıklarsak o butonun Click olayına bağlanan metodun içeriğini yazabileceğimiz bir kod penceresi açılır. Bu metodun genellikle prototipi şu şekildedir:

void Button1_OnClick(object kaynak, EventArgs e)

Kaynak, olayı hangi nesnenin çalıştırdığıdır. Yani forma bir buton getirdiğimizde, yani bir Button nesnesi oluşturduğumuzda, bu nesnenin bir adı olmalıdır. Forma buton getirmeyi kodlarla yapmadığımız için Visual Studio bu Button nesnesine button1, button2 gibi adlar verir. İşte kaynak demek bu buton nesnesinin adıdır. Button sınıfı türündendir. Kuşkusuz forma sadece buton getirmeyeceğimiz, başka form elemanları da getirebileceğimiz için bu parametre object türündedir. EventArgs ise System isim alanında bulunan bir sınıftır. Bu sınıftaki çeşitli özellikler olayla ilgili ayrıntılı bilgi içerirler. Örneğin EventArgs sınıfındaki özelliklerle kullanıcının butonun hangi koordinatlarına tıkladığını öğrenebiliriz. Biz bu metodu oluştururuz, bu metodun çağrılması ilgili Button sınıfının içinden yapılır. Dolayısıyla buradaki kaynak ve EventArgs nesnesi Button sınıfının içinde üretilir. Burada kaynağın bizim butonumuz olduğunu biliyoruz. Ancak her zaman bilemeyebiliriz. Örneğin birden fazla butonu aynı metoda bağlarsak öncelikle metodu hangi butonun çağırdığını bilmek gerekir.

.Net olayları oluşturulurken System isim alanındaki EventHandler temsilcisi kullanılır. Doğal olarak bu temsilci yukarıdaki metot prototipindedir.

Bu kitabın diğer sayfaları
  • Sınıflar
  • Operatör aşırı yükleme
  • İndeksleyiciler
  • Yapılar
  • Enum sabitleri
  • İsim alanları
  • System isim alanı
  • Temel I/O işlemleri
  • Temel string işlemleri
  • Kalıtım
  • Arayüzler
  • Partial (kısmi) tipler
  • İstisnai durum yakalama mekanizması
  • Temsilciler
  • Olaylar
  • Önişlemci komutları
  • Göstericiler
  • Assembly kavramı
  • Yansıma
  • Nitelikler
  • Örnekler
  • Şablon tipler
  • Koleksiyonlar
  • yield
  • Veri tabanı işlemleri
  • XML işlemleri
  • Form tabanlı uygulamalar
  • Visual Studio.NET
  • Çok kanallı uygulamalar
  • ASP.NET