6. پردازش رویداد
در فصل قبلی، ما مفهوم رویدادهای مرتبط با کامپوننت را مورد بحث قرار دادیم. اکنون به نحوه ایجاد رویدادها در کلاسهای خودمان میپردازیم. این یک مفهوم پیچیده است که برای توسعهدهندگان مبتدی جالب نخواهد بود و به آنها توصیه میشود مستقیماً به فصل بعدی بروند.
6.1. ابجکتهای نماینده
دستور
یک نوع به نام opération را تعریف میکند که در واقع یک پروتوتیپ تابع است که دو عدد صحیح را به عنوان ورودی میگیرد و یک عدد صحیح را برمیگرداند. این کلمه کلیدی «delegate» است که opération را به یک تعریف پروتوتیپ تابع تبدیل میکند. نوع [opération] را میتوان به صورت زیر به یک متغیر اختصاص داد:
Dim op As New opération(AddressOf [fonction])
یک متغیر از نوع [délégué] به صورت یک شی ساخته میشود. پارامتر سازنده، تابع مرتبط با نمونه دلیگیت است. در VB، این تابع به شکل AddressOf مشخص میشود، که در آن [fonction] نام تابع است. پس از ایجاد یک نمونه دِلیگیت، میتوان از آن بهعنوان یک تابع استفاده کرد:
' اجرای دلیگیت
Dim n As Integer = op(4, 7)
دیلگی [opération] به گونهای تعریف شده است که دو پارامتر از نوع [integer] را میپذیرد و نتیجهای از همان نوع را برمیگرداند. بنابراین، نمونه آن [op] که در بالا نشان داده شده است، به عنوان چنین تابعی استفاده میشود. مثال زیر را در نظر بگیرید:
' توابع محولشده
Imports System
Public Class delegate1
' تعریف یک پروتوتیپ تابع
'دو عدد صحیح را بهعنوان پارامتر میپذیرد و یک عدد صحیح برمیگرداند
Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
' دو متد نمونه که با پروتوتایپ مطابقت دارند
' اضافه
Public Function ajouter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("ajouter(" & n1 & "," & n2 & ")"))
Return n1 + n2
End Function
' تفریق
Public Function soustraire(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
Console.Out.WriteLine(("soustraire(" & n1 & "," & n2 & ")"))
Return n1 - n2
End Function
'یک متد ایستا متناظر با پروتوتایپ
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
' برنامهٔ آزمون
Public Shared Sub Main()
'یک شیء از نوع 'operation' برای ذخیره توابع تعریف میشود
' تابع استاتیک 'افزایش' ثبت میشود
Dim op As New opération(AddressOf delegate1.augmenter)
' دیلِگیت اجرا میشود
Dim n As Integer = op(4, 7)
Console.Out.WriteLine(("n=" & n))
' ایجاد یک شیء c1 از نوع class1
Dim c1 As New delegate1
' متد 'add' از c1 در دِلیگیت ثبت میشود
op = New delegate1.opération(AddressOf c1.ajouter)
' اجرای شیء نماینده
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
' متد 'subtract' از c1 در دلیگیت ذخیره میشود
op = New delegate1.opération(AddressOf c1.soustraire)
n = op(2, 3)
Console.Out.WriteLine(("n=" & n))
End Sub
End Class
نتایج اجرای آن به شرح زیر است:
6.2. مدیریت رویداد
یک شیء از نوع delegate برای چه کاری استفاده میشود؟
همانطور که در مثال زیر خواهیم دید، این عمدتاً برای مدیریت رویدادها استفاده میشود. یک کلاس از نوع 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):
Delegate Sub proc(liste d'arguments)
Public Event événement as proc
یا (۲)
Public Event événement(liste d'arguments)
کلیدواژه [Event] است که یک قطعه داده را بهعنوان رویداد تعریف میکند. امضایی که دستگیرندگان رویداد باید داشته باشند با رویداد مرتبط است. این امضا را میتوان با استفاده از یک دِلیگیت (روش ۱) یا مستقیماً (روش ۲) تعریف کرد.
6.2.2. تعریف دستکارگردانهای رویداد
هنگامی که یک رویداد توسط عبارت [RaiseEvent] فعال میشود، باید پردازش شود. روالهای مسئول رسیدگی به رویدادها، رسیدگیکنندههای رویداد نامیده میشوند. هنگامی که یک شیء [Event] ایجاد میشود، تنها امضای رسیدگیکنندههای رویداد اعلام میگردد. این امضا، امضای شیء [delegate] مرتبط با رویداد است. پس از انجام این کار، میتوان رسیدگیکنندهها را با رویداد مرتبط ساخت. اینها رویههایی هستند که باید همان امضای [delegate] مرتبط با رویداد را داشته باشند. برای مرتبط ساختن یک رسیدگیکننده رویداد با یک رویداد، از دستور [AddHandler] با سینتکس زیر استفاده کنید:
- event: متغیر رویداد که یک دستگیرکننده رویداد جدید به آن مرتبط میشود
- eventhandler: نام رویدادپرداز – باید همان امضا را داشته باشد که [délégué] مرتبط با رویداد دارد
شما میتوانید هر تعداد هاندرلری که میخواهید به یک رویداد متصل کنید. همه آنها زمانی که رویداد مربوطه فعال شود، اجرا خواهند شد.
6.2.3. راهاندازی یک رویداد
برای فعالسازی یک رویداد بهصورت برنامهای، از دستور [RaiseEvent] استفاده کنید:
- eventname: متغیر رویداد (اعلام شده با کلمه کلیدی Event)
- فهرست_آرگومانها: آرگومانهای دلیگیت مرتبط با رویداد.
تمام دستاندرکاران (handlers) که از طریق دستور AddHandler با رویداد مرتبط شدهاند، فراخوانده خواهند شد. آنها به عنوان پارامتر، پارامترهای تعریفشده در امضای رویداد را دریافت میکنند.
6.2.4. یک مثال
مثال زیر را در نظر بگیرید:
'رسیدگی به رویداد
Imports System
Public Class myEventArgs
Inherits EventArgs
'کلاس رویداد
' ویژگی
Private _saisie As String
' سازنده
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' ویژگی فقط خواندنی
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
Public Class émetteur
' کلاس تولیدکننده رویداد
' ویژگی
Private _nom As String ' nom de l'émetteur
' سازنده
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' پروتوتایپ توابعی که مسئول رسیدگی به رویداد هستند
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' مجموعهٔ رسیدگیکنندههای رویداد
Public Event evtHandler As _evtHandler
' روش درخواست صدور یک رویداد
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' تمام مشترکین مطلع میشوند
' انجام میشود گویی رویداد از اینجا نشأت گرفته است
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
' یک کلاس رسیدگی به رویداد
Public Class souscripteur
' ویژگی
Private nom As String ' nom du souscripteur
Private sender As émetteur ' l'émetteur des évts
' سازنده
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' ما نام مشترک را یادداشت میکنیم
Me.nom = nom
' و ناشر رویداد
Me.sender = e
' مشترک شدن برای دریافت رویدادها از منبع رویداد e
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' پردازشگر رویداد
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' نمایش رویداد
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
'یک برنامه آزمایشی
Public Class test
Public Shared Sub Main()
'ایجاد یک صادرکننده رویداد
Dim émetteur1 As New émetteur("émetteur1")
'ایجاد آرایهٔ مشترکین
'برای رویدادهای منتشرشده توسط emitter1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
'یک دنباله از اعداد صحیح از صفحهکلید خوانده میشود
'به محض اینکه یکی نادرست باشد، یک رویداد ارسال میشود
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
'تا زمانی که خط واردشده خالی نباشد
While saisie <> ""
'آیا ورودی یک عدد صحیح است؟
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' این یک عدد صحیح نیست
' همه مطلع میشوند
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
' همه مطلع میشوند
' ورودی جدید
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
' پایان
Environment.Exit(0)
End Sub
End Class
کد بالا نسبتاً پیچیده است. بیایید آن را تجزیه کنیم. در یک سیستم مدیریت رویداد، یک ناشر رویداد (sender) وجود دارد که جزئیات رویداد (EventArgs) را برای مشترکینی که علاقهمندی خود را به رویدادهای مورد نظر ابراز کردهاند، ارسال میکند. کلاس ناشر رویداد در اینجا به شرح زیر است:
Public Class émetteur
' کلاسی که رویدادی را ایجاد میکند
' ویژگی
Private _nom As String ' nom de l'émetteur
' سازنده
Public Sub New(ByVal nom As String)
_nom = nom
End Sub
' ToString
Public Overrides Function ToString() As String
Return _nom
End Function
' پروتوتایپ توابعی که مسئول رسیدگی به رویداد هستند
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' مجموعهٔ رسیدگیکنندههای رویداد
Public Event evtHandler As _evtHandler
' روش درخواست صدور یک رویداد
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' تمام مشترکین مطلع میشوند
' انگار رویداد از اینجا نشأت گرفته است
RaiseEvent evtHandler(Me, evt)
End Sub
End Class
هر شیء émetteur بهطور پیشفرض دارای یک nom است. متد ToString بازتعریف شده است تا زمانی که یک شیء émetteur به یک شیء string, تبدیل میشود، نام آن بازیابی شود. این کلاس یک رویداد را تعریف میکند:
' پروتوتایپ توابعی که مسئول رسیدگی به رویداد هستند
Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
' رویداد
Public Event evtHandler As _evtHandler
آرگومان اول یک تابع رسیدگی به رویداد، شیئی است که رویداد را ایجاد میکند. آرگومان دوم از نوع myEventArgs خواهد بود، ابجکتی که جزئیات مربوط به رویداد را فراهم میکند، که بعداً به آن باز خواهیم گشت. برای اینکه بتوان یک رویداد را از خارج یک شیء émetteur فراخوانی کرد، متد envoyerEvt را به کلاس اضافه میکنیم:
' روش درخواست صدور یک رویداد
Public Sub envoyerEvt(ByVal evt As myEventArgs)
' تمام مشترکین مطلع میشوند
' انجام میشود گویی رویداد از اینجا منشأ گرفته است
RaiseEvent evtHandler(Me, evt)
End Sub
ما باید انواع رویدادهایی را که توسط کلاس émetteur ایجاد خواهند شد، تعریف کنیم. برای این کار، ما کلاس myEventArgs را تعریف کردهایم:
Public Class myEventArgs
' کلاس یک رویداد
' ویژگی
Private _saisie As String
' سازنده
Public Sub New(ByVal saisie As String)
_saisie = saisie
End Sub
' ویژگی فقط-خواندنی
Public Overrides Function ToString() As String
Return _saisie
End Function
End Class
رویدادهایی که ما به آنها علاقهمند هستیم، ورودیهای نادرست صفحهکلید هستند. ما از کاربر میخواهیم که با استفاده از صفحهکلید اعداد صحیح را تایپ کند، و به محض اینکه رشتهای را تایپ کند که نمایانگر یک عدد صحیح نباشد، یک رویداد فعال خواهد شد. بهعنوان جزئیات رویداد، ما صرفاً ورودی نادرست را ارائه خواهیم داد. این هدف ویژگی _saisie کلاس است. بنابراین یک شیء myEventArgs با ورودی نادرست بهعنوان پارامتر آن ایجاد میشود. ما همچنین متد ToString را مجدداً تعریف میکنیم تا زمانی که یک شیء از نوع myEventArgs به یک رشته تبدیل میشود، ویژگی _saisie به دست آید.
ما صادرکننده رویداد و نوع رویدادهایی را که صادر میکند تعریف کردهایم. سپس نوع مشترکینی را که به این رویدادها علاقهمند هستند تعریف میکنیم.
' یک کلاس دستپرداز رویداد
Public Class souscripteur
' ویژگی
Private nom As String ' nom du souscripteur
Private sender As émetteur ' l'émetteur des évts
' سازنده
Public Sub New(ByVal nom As String, ByVal e As émetteur)
' نام مشترک ثبت میشود
Me.nom = nom
' و صادرکننده رویداد
Me.sender = e
' مشترک شدن برای دریافت رویدادها از منبع رویداد
AddHandler e.evtHandler, AddressOf traitementEvt
End Sub
' پردازشگر رویداد
Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
' نمایش رویداد
Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
End Sub
End Class
یک مشترک با دو پارامتر تعریف میشود: نام آن (ویژگی nom) و شیء émetteur که مایل است رویدادهای آن را پردازش کند (ویژگی sender). این دو پارامتر به سازنده شیء ارسال میشوند. در همین فرایند ایجاد، مشترک برای رویدادهای ناشر مشترک میشود:
'شما برای دریافت رویدادها از ناشر e مشترک میشوید
AddHandler e.evtHandler, AddressOf traitementEvt
تابعی که با ناشر ثبت شده است traitementEvt است. این متد از کلاس souscripteur دو آرگومانی را که دریافت کرده است (sender, evt) و همچنین نام گیرنده (nom) نمایش میدهد. انواع رویدادهای تولیدشده، نوع فرستنده رویداد و انواع مشترکین تعریف شدهاند. تنها کاری که باقی مانده، پیادهسازی آنهاست:
' یک برنامه آزمایشی
Public Class test
Public Shared Sub Main()
' ایجاد فرستنده رویداد
Dim émetteur1 As New émetteur("émetteur1")
' ایجاد فهرستی از مشترکین
'برای رویدادهای منتشرشده توسط emitter1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
'یک دنباله از اعداد صحیح از صفحهکلید خوانده میشود
'به محض اینکه یکی نادرست باشد، یک رویداد ارسال میشود
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
Dim saisie As String = Console.In.ReadLine().Trim()
'تا زمانی که خط واردشده خالی نباشد
While saisie <> ""
'آیا ورودی یک عدد صحیح است؟
Try
Dim n As Integer = Integer.Parse(saisie)
Catch
' این یک عدد صحیح نیست
'همه مطلع میشوند
émetteur1.envoyerEvt(New myEventArgs(saisie))
End Try
'همه مطلع میشوند
' ورودی جدید
Console.Out.Write("Nombre entier (rien pour arrêter) : ")
saisie = Console.In.ReadLine().Trim()
End While
'پایان
Environment.Exit(0)
End Sub
یک شیء émetteur ایجاد میکنیم:
' ایجاد یک صادرکننده رویداد
Dim émetteur1 As New émetteur("émetteur1")
ما یک آرایه از دو مشترک برای رویدادهایی که توسط شیء émetteur1 منتشر میشوند، ایجاد میکنیم:
' ایجاد آرایهای از مشترکین
'برای رویدادهای منتشرشده توسط emitter1
Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
از کاربر میخواهیم که اعداد صحیح را از طریق صفحهکلید وارد کند. به محض وارد شدن یک مقدار نادرست، به émetteur1 دستور میدهیم تا یک رویداد را برای مشترکین خود ارسال کند:
'همه مطلع میشوند
é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) :