Построение классов событий
В предыдущем примере мы воспользовались готовым классом System.EventArgs. Возможности этого
класса весьма ограничены, поскольку его конструктор вызывается без аргументов. При более профессиональном подходе в программе определяется новый класс события, дополняющий этот базовый класс. Например, в него можно включить ReadOnly-свойство, возвращающее информацию о предполагаемом повышении зарплаты, и другое свойство для текста сообщения. Пример подобного класса приведен ниже (решение CustomEventArgExample в архиве). Запрашиваемый рост зарплаты и сообщение инкапсулируются в конструкторе события. В дальнейшем для получения этих данных используются два свойства, доступных только для чтения:
Public Class ImproperSalaryRaiseEvent Inherits System.EventArgs Private m_Message As String Private m_theRaise As Decimal Sub New(ByVal theRaise As Decimal. ByVal theReason As String)
MyBase.New()
m_Message = theReason
m_theRaise = theRaise End Sub Readonly Property Message() As String
Get
Return m_Message
End Get End Property Readonly Property theRaise() As Decimal
Get
Return m_theRaise
End Get End Property End Class
После того как этот класс будет включен в решение, следует внести небольшие изменения в объявление события в классе Empl oyee:
Public Event SalarySecurityEvent(ByVal Sender As CustomEventArgExample.EmployeeWithEvents. ByVale As ImproperSalaryRaiseEvent)
Теперь во втором аргументе передается переменная класса ImproperSalaryRai seEvent. Следующие изменения вносятся во фрагмент, в котором непосредственно вызывается событие:
Public Overloads Sub RaiseSalary(ByVal Percent As Decimal) If Percent > LIMIT Then
' Операция запрещена - необходим пароль RaiseEvent SalarySecurityEvent(Me,
New ImproperSalaryRaiseEvent(Percent, "INCORRECT PASSWORD!")) Else
m_Salary =(1 + Percent) * m_Salary End If End Sub
Остается лишь слегка исправить код обработчика события (изменения выделены жирным шрифтом).
Module Modulel
Private WithEvents anEmployee As EmployeeWithEventsII Sub Maine)
Dim tom As New EmployeeWithEventsII("Tom". 100000) anEmployee = tom
Console.Wntel_ine(tom.TheName &"has salary " & tom.Salary) anEmployee.RaiseSalary(0.2D)'Суффикс D - признак типа Decimal Console.WriteLine(tom.TheName & "still has salary " & tom.Salary) Console.Writeline("Please press the Enter key") Console.ReadLine() End Sub
Public Sub anEmployee_SalarySecuhtyEvent(ByVal Sender _ As CustomEventArgExample.EmployeeWithEvents. ByVal e As CustomEventArgExample.ImproperSalaryRaiseEvent) Handles anEmployee.SalarySecurityEvent
MsgBox(Sender.TheName & "had an improper salary raise of " & _ FormatPercent(e.theRaise) & "with INCORRECT PASSWORD!") End Sub End Module
Результат показан на следующем рисунке. Как видно из рисунка, данные о запрошенном росте заработной платы доступны в обработчике события.
Динамическая
обработка событий
Конечно, для установки обработчика события необходимо зарегистрировать не только класс-приемник, но и метод, который должен вызываться при возникновении события. Для этой цели применяется команда AddHandler, которой при вызове передаются два параметра:
- имя события в классе-источнике;
- адрес метода (процедуры
событий) класса-приемника, вызываемого при возникновении события.
tom:
AddHandler tom.SalarySecurityEvent.AddressOf anEmp1oyee_SalarySecurityEvent
В результате тестовая программа будет обнаруживать событие Sal arySecuri tyEvent объекта tom и в случае его возникновения — вызывать процедуру anEmployee_SalarySecurityEvent текущего модуля (разумеется, процедура anEmployee_SalarySecurityEvent должна обладать правильной сигнатурой!).
Ниже приведен фрагмент решения AddHandlerExamplel (ключевые строки выделены жирным шрифтом):
Module Modulel
Private WithEvents anEmployee As EmployeeWithEvents Sub Main()
Dim torn As New EmployeeWithEvents("Tom". 100000) Console.WriteLine(tom.TheName & "has salary " & tom.Salary) AddHandler tom.SalarySecurityEvent, AddressOf anEmployee_SalarySecurityEvent tom.RaiseSalary(0.2D) ' Суффикс D - признак типа Decimal Console.WriteLine(tom.TheName & "still has salary " & tom.Salary) Console.WriteLine("Please press the Enter key") Console. ReadLine() End Sub
Public Sub anEmployee_SalarySecurity£vent(ByVal Sender _ As AddHandlerExamplel.EmployeeWi thEvents,_ ByVal e As AddHandlerExamplel.ImproperSalaryRaiseEvent)_ Handles anEmployee.SalarySecurityEvent MsgBox(Sender.TheName & "had an improper salary raise of " & _
FormatPercent(e.theRaise) & "with INCORRECT PASSWORD!") End Sub End Module
Команда AddHandler обладает просто невероятной гибкостью. Например, установка обработчиков событий может зависеть от имени типа:
If TypeName(tom)="Manager" Then
AddHandler tom.SalarySecurityEvent.AddressOf _ anEmployee_SalarySecurityEvent e
End If
Кроме того, один обработчик событий можно связать с несколькими разными событиями, происходящими в разных классах. Это позволяет выполнять в VB .NET централизованную обработку событий с динамическим назначением обработчиков — в VB такая возможность встречается впервые. В приведенном ниже листинге инициируются разные события в зависимости от переданных параметров командной строки. Главное место в нем занимают фрагменты вида
Case "first"
AddHandler m_EventGenerator.TestEvent,_ AddressOf m_EventGenerator_TestEventl
При передаче в командной строке аргумента first устанавливается соответствующий обработчик события.
В программе используется полезный метод GetCommandLineArgs класса System.Environment. Как упоминалось в главе 3, этот метод возвращает массив аргументов командной строки. Начальный элемент массива содержит имя исполняемого файла; поскольку индексация массива начинается с 0, для получения первого аргумента используется вызов System.Environment.GetComman3LineArgs(l), однако предварительно необходимо убедиться в существовании аргументов командной строки, для чего проверяется длина массива System.Environment.GetCommandLineArgs. Перед запуском программы перейдите на страницу Configuration Properties диалогового окна Project Properties и укажите аргументы командной строки для тестирования.
Ниже приведен полный исходный текст программы:
Option Strict On Module Modulel
Private m_EventGenerator As EventGenerator Sub Main()
m_EventGenerator= New EventGenerator()
Dim commandLinesOAs String = System.Environment.GetCommandLineArgs
If commandLines.Length = 1 Then
MsgBox("No command argument.program ending!") Environment.Exit(-l) Else
Dim theCommand As String = commandLines(l) Console.WriteLine("Thecommand lineoption is" StheCommand) ' Проверить параметр командной строки и назначить ' соответствующий обработчик события. Select Case theCommand Case "first"
AddHandler m_EventGenerator.TestEvent. AddressOf m_EventGenerator_TestEvent1 Case "second"
AddHandler m_EventGenerator.TestEvent,_ AddressOf m_EventGenerator_TestEvent2 Case Else
AddHandler m_EventGenerator.TestEvent. AddressOf m_EventGenerator_TestEventDefault End Select
' Инициировать события m_EventGenerator.TriggerEvents() End If
Console.WriteLine("Press enter to end.") Console. ReadLine() End Sub
'Обработчик по умолчанию для непустой командной строки Public Sub m_EventGenerator_TestEventDefault(_
ByVal sender As Object.ByVal evt As EventArgs) System.Console.WriteLine("Default choice " & _
m_EventGenerator.GetDescri pti on()) End Sub
' Обработчик 12 для строки "first" Public Sub m_EventGenerator_TestEvent1(_
ByVal sender As Object.ByVal evt As EventArgs) System.Console.WriteLineC'lst choice " & _
m_EventGenerator.GetDescription()) End Sub
'Обработчик 13 для строки "second" Public Sub m_EventGenerator_TestEvent2(
ByVal sender As Object.ByVal evt As EventArgs) System.Console.WriteLinet"2nd choice " & _ m_EventGenerator.GetDescri pti on ())
End Sub End Module Public Class EventGenerator
' В классе определяется только одно событие
Public Event TestEvent(ByVal sender As Object, ByValevt As EventArgs)
' Также можно было использовать конструктор по умолчанию
Public Sub New()
' Пустой конструктор
End Sub
.Public Function GetDescription() As String
Return "EventGenerator class" End Function
' Процедура вызывается для инициирования событий Public Sub TriggerEvents()
Dim e As System.EventArgs = New System.EventArgs() RaiseEvent TestEvent(Me.e) End Sub End Class