6. معالجة الأحداث
في الفصل السابق، ناقشنا مفهوم الأحداث المرتبطة بالمكونات. سنرى الآن كيفية إنشاء الأحداث في فئاتنا الخاصة. هذا مفهوم صعب لن يثير اهتمام المطورين المبتدئين، الذين يُنصحون بالانتقال مباشرة إلى الفصل التالي.
6.1. كائنات التفويض
العبارة
يحدد نوعًا يسمى operation، وهو في الواقع نموذج أولي لدالة تقبل عددين صحيحين وتُرجع عددًا صحيحًا. إن الكلمة الرئيسية delegate هي التي تجعل من operation تعريفًا لنموذج أولي لدالة. يمكن تعيين النوع [operation] إلى متغير على النحو التالي:
Dim op As New opération(AddressOf [fonction])
يتم إنشاء متغير من النوع [delegate] ككائن. معلمة المنشئ هي الدالة المرتبطة بمثيل delegate. في VB، يتم تحديد هذه الدالة على أنها AddressOf function، حيث [function] هو اسم الدالة. بمجرد إنشاء مثيل delegate، يمكن استخدامه كدالة:
' the delegate is executed
Dim n As Integer = op(4, 7)
تم تعريف الوفد [operation] ليأخذ معلمتين من النوع [integer] ويعيد نتيجة من نفس النوع. لذلك، يتم استخدام مثيله [op] أعلاه كدالة من هذا النوع. انظر المثال التالي:
' delegated functions
Imports System
Public Class delegate1
' function prototype definition
' accepts 2 integers as parameters and returns an integer
Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
' two instance methods corresponding to the prototype
' add
Public Function ajouter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("ajouter(" & n1 & "," & n2 & ")"))
Return n1 + n2
End Function
' subtract
Public Function soustraire(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("soustraire(" & n1 & "," & n2 & ")"))
Return n1 - n2
End Function
' a static method corresponding to the prototype
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()
' define an operation object to store functions
' we register the static function increase
Dim op As New opération(AddressOf delegate1.augmenter)
' the delegate is executed
Dim n As Integer = op(4, 7)
Console.Out.WriteLine(("n=" & n))
' creation of a c1 object of type class1
Dim c1 As New delegate1
' we register c1's add method in the delegate
op = New delegate1.opération(AddressOf c1.ajouter)
' execution of delegated object
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
' we register the subtract method of c1 in the delegate
op = New delegate1.opération(AddressOf c1.soustraire)
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
End Sub
End Class
نتائج التنفيذ هي كما يلي:
6.2. معالجة الأحداث
ما هي الاستخدامات الممكنة لكائن الوكيل؟
وكما سنرى في المثال التالي، يُستخدم هذا بشكل أساسي لمعالجة الأحداث. يمكن لفئة C1 إنشاء أحداث evti. وعندما يحدث حدث evti، يقوم كائن من النوع C1 بتشغيل كائن evtiDéclenché من النوع delegate. سيتم بعد ذلك تنفيذ جميع الوظائف المسجلة في كائن الوكيل evtiDéclenché. إذا أراد كائن C2 يستخدم كائن C1 أن يتم إخطاره بحدوث حدث evti على الكائن C1، فسيقوم بتسجيل إحدى طرقه، C2.f، مع كائن الوكيل C1.evtiDéclenché الخاص بالكائن C1 بحيث يتم تنفيذ الطريقة C2.f في كل مرة يحدث فيها حدث evti على الكائن C1. نظرًا لأن كائن الوكيل C1.evtiDéclenché يمكنه تسجيل وظائف متعددة، يمكن لكائنات Ci المختلفة التسجيل مع الوكيل C1.evtiDéclenché لتلقي إشعار بحدوث الحدث evti على C1.
6.2.1. إعلان حدث
يتم إعلان الحدث على النحو التالي (1):
أو (2)
تُعرّف الكلمة الرئيسية [Event] جزءًا من البيانات على أنه حدث. ويرتبط الحدث بالتوقيع الذي يجب أن تمتلكه معالجاته. ويمكن تعريف هذا التوقيع باستخدام مندوب (الطريقة 1) أو بشكل مباشر (الطريقة 2).
6.2.2. تحديد معالجات الأحداث
عندما يتم تشغيل حدث بواسطة عبارة [RaiseEvent]، يجب معالجته. تسمى الإجراءات المسؤولة عن معالجة الأحداث بمعالجات الأحداث. عند إنشاء كائن [Event]، يتم إعلان توقيع معالجات الأحداث فقط. هذا التوقيع هو توقيع كائن [delegate] المرتبط بالحدث. بمجرد الانتهاء من ذلك، يمكن ربط المعالجات بالحدث. هذه إجراءات يجب أن يكون لها نفس التوقيع مثل [delegate] المرتبط بالحدث. لربط معالج حدث بحدث، استخدم عبارة [AddHandler] بالصيغة التالية:
- event: متغير الحدث الذي يتم ربط معالج الحدث الجديد به
- معالج الحدث: اسم معالج الحدث — يجب أن يكون له نفس التوقيع مثل [المندوب] المرتبط بالحدث
يمكنك ربط أي عدد تريده من المعالجات بحدث ما. وسيتم تنفيذها جميعًا عند تشغيل الحدث المرتبط بها.
6.2.3. تشغيل حدث
لتشغيل حدث برمجياً، استخدم عبارة [RaiseEvent]:
- eventname: متغير الحدث (المعلن باستخدام الكلمة الرئيسية Event)
- قائمة_الحجج: حجج المندوب المرتبط بالحدث.
سيتم استدعاء جميع المعالجات التي تم ربطها بالحدث باستخدام عبارة AddHandler. وستتلقى كمعلمات المعلمات المحددة في توقيع الحدث.
6.2.4. مثال
لنأخذ المثال التالي:
'event management
Imports System
Public Class myEventArgs
Inherits EventArgs
' the class of an evt
' attribute
Private _saisie As String
' manufacturer
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' read-only property
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
Public Class émetteur
' the class issuing an event
' attribute
Private _nom As String ' name of issuer
' manufacturer
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' the prototype of the functions responsible for processing the event
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' pool of event managers
Public Event evtHandler As _evtHandler
' method of requesting the issue of an evt
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' all subscribers are notified
' we act as if the event originated here
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
' an evt treatment class
Public Class souscripteur
' attribute
Private nom As String ' name of subscriber
Private sender As émetteur ' evts sender
' manufacturer
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' we note the name of the subscriber
Me.nom = nom
' and the sender of the evts
Me.sender = e
' register to receive news from the transmitter e
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' event manager
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' evt display
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
' a test program
Public Class test
Public Shared Sub Main()
' creation of an evts transmitter
Dim émetteur1 As New émetteur("émetteur1")
' create a table of subscribers
' for events issued by issuer1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
' read a sequence of integers from the keyboard
' as soon as one is wrong, we issue an evt
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
' as long as the line entered is not empty
While saisie <> ""
' is the input a whole number?
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' not an integer
' we warn everyone
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
' we warn everyone
' new entry
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
' end
Environment.Exit(0)
End Sub
End Class
الرمز أعلاه معقد إلى حد ما. دعونا نحلله. في معالجة الأحداث، يوجد مرسل للأحداث يقوم بإرسال تفاصيل الحدث (EventArgs) إلى المشتركين الذين أبدوا اهتمامًا بالأحداث المعنية. فئة إرسال الأحداث هنا هي كما يلي:
Public Class émetteur
' the class issuing an event
' attribute
Private _nom As String ' name of issuer
' manufacturer
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' the prototype of the functions responsible for processing the event
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' pool of event managers
Public Event evtHandler As _evtHandler
' method of requesting the issue of an evt
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' all subscribers are notified
' we act as if the event originated here
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
يحتوي كل كائن باعث على اسم محدد افتراضيًا. تمت إعادة تعريف طريقة ToString بحيث يتم إرجاع اسم كائن الباعث عند تحويله إلى سلسلة. تحدد الفئة حدثًا:
' the prototype of the functions responsible for processing the event
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' the event
Public Event evtHandler As _evtHandler
ستكون الحجة الأولى لدالة معالج الحدث هي الكائن الذي يصدر الحدث. وستكون الحجة الثانية من النوع myEventArgs، وهو كائن يوفر تفاصيل حول الحدث، وسنناقشه لاحقًا. لتشغيل حدث من خارج كائن المصدر، نضيف طريقة envoyerEvt إلى الفئة:
' method of requesting the issue of an evt
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' all subscribers are notified
' we act as if the event originated here
RaiseEvent evtHandler(Me, evt)
End Sub
نحتاج إلى تحديد نوع الأحداث التي تطلقها فئة emitter. للقيام بذلك، قمنا بتعريف فئة myEventArgs:
Public Class myEventArgs
' the class of an evt
' attribute
Private _saisie As String
' manufacturer
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' read-only property
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
الأحداث التي تهمنا هي المدخلات الخاطئة من لوحة المفاتيح. سنطلب من المستخدم كتابة أعداد صحيحة على لوحة المفاتيح، وبمجرد أن يكتب سلسلة لا تمثل عددًا صحيحًا، سنقوم بتشغيل حدث. وكتفاصيل للحدث، سنقدم ببساطة المدخلة الخاطئة. وهذا هو الغرض من السمة _input الخاصة بالفئة. وبالتالي، يتم إنشاء كائن myEventArgs مع المدخلة الخاطئة كمعلمة له. كما نقوم بتجاوز طريقة ToString بحيث عندما نقوم بتحويل كائن myEventArgs إلى سلسلة، نحصل على الخاصية _input.
لقد حددنا مصدر الحدث ونوع الأحداث التي يصدرها. بعد ذلك، نحدد نوع المشتركين المهتمين بهذه الأحداث.
' an evt treatment class
Public Class souscripteur
' attribute
Private nom As String ' name of subscriber
Private sender As émetteur ' evts sender
' manufacturer
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' we note the name of the subscriber
Me.nom = nom
' and the sender of the evts
Me.sender = e
' register to receive news from the transmitter e
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' event manager
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' evt display
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
يتم تعريف المشترك بواسطة معلمتين: اسمه (سمة name) وكائن الباعث الذي يريد معالجة أحداثه (سمة sender). يتم تمرير هاتين المعلمتين إلى منشئ الكائن. وأثناء عملية الإنشاء هذه، يشترك المشترك في أحداث الباعث:
' on s'inscrit pour recevoir les evts de l'émetteur e
AddHandler e.evtHandler, AddressOf traitementEvt
الوظيفة المسجلة لدى الناشر هي traitementEvt. تعرض هذه الطريقة لفئة المشترك الحجتين اللتين تلقتهما (sender, evt) بالإضافة إلى اسم المستقبل (nom). تم تعريف أنواع الأحداث المنتجة، ونوع ناشر هذه الأحداث، وأنواع المشتركين. كل ما تبقى هو تنفيذها:
' a test program
Public Class test
Public Shared Sub Main()
' creation of an evts transmitter
Dim émetteur1 As New émetteur("émetteur1")
' create a table of subscribers
' for events issued by issuer1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
' read a sequence of integers from the keyboard
' as soon as one is wrong, we issue an evt
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
' as long as the line entered is not empty
While saisie <> ""
' is the input a whole number?
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' not an integer
' we warn everyone
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
' we warn everyone
' new entry
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
' end
Environment.Exit(0)
End Sub
نقوم بإنشاء كائن مُصدر أحداث:
' creation of an evts transmitter
Dim émetteur1 As New émetteur("émetteur1")
نقوم بإنشاء مصفوفة من مشتركين اثنين للأحداث التي يصدرها الكائن emetteur1:
' create a table of subscribers
' for events issued by issuer1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
نطلب من المستخدم إدخال أعداد صحيحة عبر لوحة المفاتيح. بمجرد إدخال قيمة غير صحيحة، نطلب من issuer1 إرسال حدث إلى مشتركيه:
' on prévient tout le monde
émetteur1.envoyerEvt(New myEventArgs(saisie))
الحدث المرسل هو من النوع myEventArgs ويحتوي على الإدخال غير الصحيح. يجب أن يتلقى كلا المشتركين هذا الحدث ويبلغا عنه. يظهر ذلك في التنفيذ التالي.
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) :