6. Olay Yönetimi
Bir önceki bölümde bileşenlerle ilgili olay kavramını ele almıştık. Şimdi kendi sınıflarımızda nasıl olaylar oluşturacağımızı göreceğiz. Bu zor bir konudur ve yeni başlayan geliştiricilerin ilgisini çekmeyecektir; bu nedenle yeni başlayan geliştiricilerin doğrudan bir sonraki bölüme geçmeleri tavsiye edilir.
6.1. Delegate nesneleri
Şu komut:
komutu, opération adlı bir türü tanımlar; bu tür, aslında iki tamsayı alan ve bir tamsayı döndüren bir işlev prototipidir. opération'i bir işlev prototipi tanımı yapan, delegate anahtar sözcüğüdür. [opération] türü, bir değişkene şu şekilde atanabilir:
Dim op As New opération(AddressOf [fonction])
[délégué] türündeki bir değişken, bir nesne olarak oluşturulur. Oluşturucunun parametresi, delege örneğiyle ilişkili işlevdir. VB'te bu işlev, AddressOf işlevi biçiminde verilir; burada [fonction] işlevin adıdır. Bir delege örneği oluşturulduktan sonra, bir işlev olarak kullanılabilir:
' delege işlevi çalıştırılıyor
Dim n As Integer = op(4, 7)
[opération] delege, [integer] türünde iki parametre alacak ve aynı türde bir sonuç döndürecek şekilde tanımlanmıştır. Dolayısıyla, yukarıdaki [op] örneği bu tür bir işlev olarak kullanılır. Aşağıdaki örneği ele alalım:
' delege edilmiş işlevler
Imports System
Public Class delegate1
' fonksiyon prototipi tanımı
' 2 tamsayı parametre alır ve bir tamsayı döndürür
Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
' prototipe karşılık gelen iki örnek yöntemi
' ekle
Public Function ajouter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("ajouter(" & n1 & "," & n2 & ")"))
Return n1 + n2
End Function
' çıkarma
Public Function soustraire(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("soustraire(" & n1 & "," & n2 & ")"))
Return n1 - n2
End Function
' prototipe karşılık gelen bir statik yöntem
Public Shared Function augmenter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("augmenter(" & n1 & "," & n2 & ")"))
Return n1 + 2 * n2
End Function
' test programı
Public Shared Sub Main()
' fonksiyonları kaydetmek için bir işlem türü nesnesi tanımlanır
' "artır" statik fonksiyonu kaydedilir
Dim op As New opération(AddressOf delegate1.augmenter)
' delege çalıştırılıyor
Dim n As Integer = op(4, 7)
Console.Out.WriteLine(("n=" & n))
' class1 türünde bir c1 nesnesi oluşturulur
Dim c1 As New delegate1
' c1 nesnesinin "ekle" yöntemini delegeye kaydedilir
op = New delegate1.opération(AddressOf c1.ajouter)
' delege nesnesinin çalıştırılması
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
' c1'in "çıkarma" yöntemini delegeye kaydediliyor
op = New delegate1.opération(AddressOf c1.soustraire)
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
End Sub
End Class
Çalıştırma sonuçları şöyledir:
6.2. Olay yönetimi
delegate türündeki bir nesne ne işe yarar?
Aşağıdaki örnekte göreceğimiz gibi, bu nesne esas olarak olay yönetiminde kullanılır. Bir C1 sınıfı, evti olaylarını oluşturabilir. Bir evti olayı sırasında, C1 türündeki bir nesne, delegate türündeki bir evtiDéclenché nesnesinin yürütülmesini başlatır. Böylece, evtiDéclenché delegasyon nesnesinde kayıtlı tüm işlevler yürütülecektir. C1 nesnesini kullanan bir C2 nesnesi, C1 nesnesinde evti olayının meydana geldiği konusunda bilgilendirilmek isterse, C1 nesnesinin vekil nesnesi olan C1.evtiDéclenché'e kendi C2.f yöntemlerinden birini kaydedecektir; böyleceC1 nesnesinde evti olayı her gerçekleştiğinde C2.f yöntemi çalıştırılsın. C1.evtiDéclenché delege nesnesi birden fazla işlev kaydedebileceğinden, farklı Ci nesneleri, C1 üzerindeki evti olayından haberdar olmak için C1.evtiDéclenché delege nesnesine kaydolabilir.
6.2.1. Bir olayın bildirilmesi
Bir olay şu şekilde bildirilir (1):
Delegate Sub proc(liste d'arguments)
Public Event événement as proc
ya da (2)
Public Event événement(liste d'arguments)
Bir veriyi olay olarak tanımlayan anahtar kelime [Event]'tir. Olayla, bu olayı işleyecek işleyicilerin sahip olması gereken imza ilişkilendirilir. Bu imza, bir delege aracılığıyla (yöntem 1) veya doğrudan (yöntem 2) tanımlanabilir.
6.2.2. Bir olayın işleyicilerini tanımlama
[RaiseEvent] komutuyla bir olay tetiklendiğinde, bu olayın işlenmesi gerekir. Olayları işlemekle görevli prosedürlere olay işleyicileri denir. Bir [Event] nesnesi oluşturulduğunda, yalnızca olay işleyicilerinin imzası bildirilir. Bu imza, olayla ilişkilendirilmiş [delegate] nesnesinin imzasını taşır. Bu işlem tamamlandıktan sonra, olaya işleyiciler ilişkilendirilebilir. Bu işleyiciler, olayla ilişkili [delegate] nesnesiyle aynı imzaya sahip olmalıdır. Bir olay işleyicisini bir olaya ilişkilendirmek için, aşağıdaki sözdizimine sahip [AddHandler] komutu kullanılır:
- event: yeni bir olay işleyicisinin ilişkilendirileceği olay değişkeni
- eventhandler: olay işleyicisinin adı - olayla ilişkilendirilen [délégué] komutuyla aynı imzaya sahip olmalıdır
Bir olaya istediğiniz kadar işleyici atayabilirsiniz. Bunlar, atandıkları olay tetiklendiğinde hepsi çalıştırılacaktır.
6.2.3. Bir olayı tetikleme
Program yoluyla bir olayı tetiklemek için [RaiseEvent] komutu kullanılır:
- eventname: olay değişkeni (Event anahtar sözcüğüyle tanımlanır)
- argumentlist: olaya atanan delegenin argümanları.
AddHandler komutuyla olaya ilişkilendirilmiş tüm işleyiciler çağrılacaktır. Bu işleyiciler, olayın imzasında tanımlanan parametreleri parametre olarak alacaktır.
6.2.4. Bir örnek
Aşağıdaki örneği ele alalım:
'olay yönetimi
Imports System
Public Class myEventArgs
Inherits EventArgs
' bir olayın sınıfı
' özellik
Private _saisie As String
' yapıcı
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' salt okunur giriş özelliği
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
Public Class émetteur
' bir olayın gönderen sınıfı
' özellik
Private _nom As String ' nom de l'émetteur
' yapıcı
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' olayı işleyen fonksiyonların prototipi
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' olay işleyicileri havuzu
Public Event evtHandler As _evtHandler
' bir olayın yayınlanmasını talep etme yöntemi
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' tüm abonelere bildirim gönderilir
' olay buradan geliyormuş gibi davranılır
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
' bir olay işleme sınıfı
Public Class souscripteur
' özellik
Private nom As String ' nom du souscripteur
Private sender As émetteur ' l'émetteur des évts
' yapıcı
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' abone adını not ediyoruz
Me.nom = nom
' ve olayların yayıncısını
Me.sender = e
' e olay yayıncısından olayları almak için kaydolun
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' olay yöneticisi
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' olay görüntüleme
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
' bir test programı
Public Class test
Public Shared Sub Main()
' bir olay vericisinin oluşturulması
Dim émetteur1 As New émetteur("émetteur1")
' abone tablosu oluşturma
' yayıncı1 tarafından yayınlanan olaylar için
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
' klavyeden bir dizi tamsayı okunur
' Bir tanesi hatalı olur olmaz bir olay yayınlanır
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
' girdilen satır boş olmadığı sürece
While saisie <> ""
' Girilen değer bir tamsayı mı?
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' bu bir tamsayı değil
' herkese uyarı verilir
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
' herkese uyarı verilir
' yeni giriş
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
' son
Environment.Exit(0)
End Sub
End Class
Yukarıdaki kod oldukça karmaşıktır. Bunu ayrıntılı olarak inceleyelim. Bir olay yönetiminde, söz konusu olaylara ilgi duyduğunu beyan eden abonelere olay ayrıntılarını (EventArgs) gönderen bir olay yayıncısı (sender) vardır. Burada olayları yayınlayan sınıf şöyledir:
Public Class émetteur
' bir olayın gönderen sınıfı
' özellik
Private _nom As String ' nom de l'émetteur
' oluşturucu
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' olayı işleyen fonksiyonların prototipi
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' olay işleyicileri havuzu
Public Event evtHandler As _evtHandler
' bir olayın yayınlanmasını talep etme yöntemi
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' tüm abonelere bildirim gönderilir
' olay buradan geliyormuş gibi davranılır
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
Her émetteur nesnesinin, yapısal olarak belirlenmiş bir nom'i vardır. ToString yöntemi, bir émetteur nesnesini string,'e dönüştürürken bu nesnenin adının alınacağı şekilde yeniden tanımlanmıştır. Sınıf, bir olay tanımlar:
' olayı işleyen fonksiyonların prototipi
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' olay
Public Event evtHandler As _evtHandler
Bir olay işleme fonksiyonunun ilk argümanı, olayı tetikleyen nesne olacaktır. İkinci argüman, olayla ilgili ayrıntıları veren ve daha sonra tekrar ele alacağımız myEventArgs türünde bir nesne olacaktır. Bir émetteur nesnesinin dışından bir olayı tetikleyebilmek için, sınıfa envoyerEvt yöntemini ekliyoruz:
' bir olayın yayınlanmasını talep etme yöntemi
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' tüm abonelere bildirim gönderilir
' olay buradan geliyormuş gibi davranılır
RaiseEvent evtHandler(Me, evt)
End Sub
émetteur sınıfı tarafından tetiklenecek olayların türünü tanımlamamız gerekiyor. Bunun için myEventArgs sınıfını tanımladık:
Public Class myEventArgs
' bir olayın sınıfı
' özellik
Private _saisie As String
' yapıcı
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' salt okunur giriş özelliği
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
Bizi ilgilendiren olaylar, klavyeden yapılan hatalı girişlerdir. Bir kullanıcıdan klavyeden tamsayılar girmesini isteyeceğiz ve tamsayı olmayan bir dize girdiğinde bir olay tetiklenecektir. Olayın detayı olarak, hatalı girişi belirtmekle yetineceğiz. Sınıfın _saisie özniteliğinin amacı budur. Dolayısıyla, hatalı girişi parametre olarak alan bir myEventArgs nesnesi oluşturulur. Ayrıca, bir myEventArgs nesnesini dizgeye dönüştürdüğümüzde _saisie özniteliğini elde edebilmek için ToString yöntemini yeniden tanımlıyoruz.
Olayların kaynağını ve kaynağın yaydığı olay türlerini tanımladık. Ardından, bu olaylarla ilgilenen abone türlerini tanımlıyoruz.
' olay işleme sınıfı
Public Class souscripteur
' özellik
Private nom As String ' nom du souscripteur
Private sender As émetteur ' l'émetteur des évts
' oluşturucu
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' abone adı kaydedilir
Me.nom = nom
' ve olayların yayıncısını
Me.sender = e
' e olay vericisinin olaylarını almak için kaydolun
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' olay yöneticisi
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' olay görüntüleme
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
Bir abone, iki parametre ile tanımlanacaktır: adı (nom özniteliği) ve olaylarını işlemek istediği émetteur nesnesi (sender özniteliği). Bu iki parametre, nesnenin oluşturucusuna aktarılacaktır. Aynı oluşturma işlemi sırasında, abone yayıncının olaylarına abone olur:
' yayıncının olaylarını almak için kayıt oluruz
AddHandler e.evtHandler, AddressOf traitementEvt
Yayına kaydedilen işlev traitementEvt'tir. souscripteur sınıfının bu yöntemi, aldığı iki argümanı (sender, evt) ve alıcının adını (nom) görüntüler. Oluşturulan olayların türü, bu olayların göndereninin türü ve abonelerin türü tanımlanmıştır. Artık bunları uygulamaya koymamız gerekiyor:
' bir test programı
Public Class test
Public Shared Sub Main()
' olay vericisi oluşturma
Dim émetteur1 As New émetteur("émetteur1")
' abone tablosu oluşturma
' yayıncı1 tarafından yayınlanan olaylar için
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
' klavyeden bir dizi tamsayı okunur
' Bir tanesi hatalı olur olmaz bir olay yayınlanır
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
' girdilen satır boş olmadığı sürece
While saisie <> ""
' Girilen değer bir tamsayı mı?
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' bu bir tamsayı değil
' herkese uyarı verilir
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
' herkese uyarı verilir
' yeni giriş
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
' son
Environment.Exit(0)
End Sub
Bir émetteur nesnesi oluşturuyoruz:
' bir olay yayınlayıcısı oluşturuluyor
Dim émetteur1 As New émetteur("émetteur1")
émetteur1 nesnesi tarafından yayınlanan olaylar için iki abone içeren bir tablo oluşturuyoruz:
' abone tablosu oluşturma
' yayıncı1 tarafından yayınlanan olaylar için
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
Kullanıcıdan klavyeyle tamsayılar girmesini istiyoruz. Hatalı bir giriş yapıldığında, émetteur1 nesnesinden abonelerine bir olay göndermesini istiyoruz:
' herkese bildirim gönderilir
émetteur1.envoyerEvt(New myEventArgs(saisie))
Gönderilen olay, myEventArgs türündedir ve hatalı girişi içerir. Her iki abone de bu olayı almalı ve bildirmelidir. Aşağıdaki çalıştırma örneği bunu göstermektedir.
dos>evt1
Nombre entier (rien pour arrêter) : 4
Nombre entier (rien pour arrêter) : a
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s2]
Nombre entier (rien pour arrêter) : 1.6
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s2]
Nombre entier (rien pour arrêter) :