Простейшее инициирование событий

Давайте вернемся к простому классу Empl oyee и подробно, шаг за шагом разберем все, что необходимо сделать для определения и инициирования событий. Предположим, событие должно инициироваться при попытке увеличения заработной платы более чем на 10 процентов без ввода пароля. В главе 4 метод RaiseSalary выглядел так:

Public Overloads Sub RaiseSalary(ByVal percent As Decimal)

If percent > LIMIT Then

' Операция запрещена - Необходим пароль

Console.WriteLine("MUST HAVE PASSWORD TO RAISE SALARY " & _

"MORE THAN LIMIT!!!!") Else

m_Sa1ary =(1 + percent) * m_salary

End If

End Sub

Вместо выделенной команды, выводящей текстовое сообщение на консоль, должно инициироваться событие. Задача решается в несколько этапов. В простейшем случае в классе сначала объявляется открытая переменная с ключевым словом Event, с указанием имени события и его параметров. Например, следующая строка весьма близка к синтаксису VB6: Public Event SalarySecurityEventdnessage as String) В этой строке объявляется открытое событие с параметром строкового типа.

События обычно объявляются с модификатором Public, никогда не возвращают значений и могут получать параметры произвольного типа, кроме ParamArray.

После того как переменная события будет определена, событие инициируется командой следующего вида (впрочем, для того, чтобы событие действительно произошло, потребуются еще кое-какие дополнительные действия):

RaiseEvent SalarySecurityEventC'MUST HAVE PASSWORD TO RAISE " & _

"Salary MORE THAN LIMIT!! !!")

Однако из этого не следует, что для любого события следует ограничиваться одним строковым параметром. В соответствии с парадигмой программирования .NET в качестве параметров любого события передается объект-источник и информация о событии, инкапсулированная в объекте события. На первых порах вполне достаточно объявления вида

Public Event SalarySecurityEvent(ByVal who As Employee, ByVale As system.EventArgs)

Событие инициируется следующей командой RaiseEvent:

RaiseEvent SalarySecurityEvent(Me,New System.EventArgs())

Хотя события обычно объявляются открытыми, это не является обязательным требованием — событие может иметь любой модификатор уровня доступа. Закрытыми (Private) объявляются события, представляющие интерес только для объектов этого класса, а защищенные (Protected) события также могут обрабатываться объектами производных классов. Допускается даже объявление общих (Shared) событий, которые, как и общие члены классов, существуют на уровне класса в целом, а не его отдельных членов (в частности, общие методы могут инициировать только общие события).

По сигнатуре события приемник узнает, от какого источника поступило событие (в данном примере это объект-работник, которому попытались неправильно повысить заработную плату); сам объект передается в виде ключевого слова Me. Впрочем, приведенное объявление не использует возможностей передачи данных в переменной события е. Вскоре мы разработаем класс, производный от System. EventArgs, в объектах которого будет содержаться строка предупреждения вместе с данными о попытке повышения заработной платы.

 

Подключение приемников к источнику

В нашем распоряжении имеется весь код, необходимый для рассылки событий, но пока нет ни одного заинтересованного получателя. Существует несколько способов, которыми класс может сообщить VB .NET о своем желании получать события от другого класса. Простейший способ очень похож на тот, который использовался в VB6: на уровне модуля (или класса) объявляется переменная класса-приемника с ключевым словом WithEvents. Например, если включить в класс следующую строку, не входящую ни в один из членов: Private WithEvents anEmployee As Employee

объекты этого класса становятся потенциальными приемниками событий, инициируемых классом Employee. Обратите особое внимание на некоторые особенности этого объявления:

  • Класс источника должен быть указан явно, объявления вида As Object недопустимы.
  • Объявление располагается на уровне модуля или класса и не содержит ключевого слова New.
  • После включения этой строки в программу объектная переменная anEmpl oyee может использоваться всюду, где вас интересует событие SalarySecurityEvent. Как показано на рис. 6.2, IDE автоматически создает обработчик события с именем, построенным по схеме А_В, для каждой объектной переменной, объявленной с ключевым словом Wi thEvents. Чтобы вызвать автоматически сгенерированный «скелет» события, достаточно выбрать его в раскрывающемся списке, как на рис. 6.2.

     

    Все вместе

    А теперь давайте объединим все сказанное на практическом примере. Создайте консольное приложение и включите следующий фрагмент в первый (стартовый) модуль:

    Module Modulel

    Private WithEvents anEmployee As EmployeeWithEvents

    Sub Main()

    Dim tom As New EmployeeWithEvents("Tom". 100000)

    anEmployee = tom

    Console.WriteLine(tom.TheName & "has salary " & tom.Salary)

    anEmployee.RaiseSalary(0.2D) ' Суффикс D - признак типа Decimal

    Console.WriteLinettom.TheName & "still has salary " & tom.Salary)

    Console.WritelineC'Please press the Enter key")

    Console.ReadLine() End Sub End Module

    Рис. 6.2. Автоматически сгенерированный код обработчика события

    Теперь выберите в раскрывающемся списке метод anEmployee_SalarySecurityEvent. Исходный текст этого метода приведен ниже (для удобства чтения он разбит на несколько строк, а ключевая секция Handles выделена жирным шрифтом):

    Public Sub anEmployee_SalarySecur1tyEvent(ByVal Sender As

    Event_Handling_I.EmployeeWithEvents, ByValeAs System.EventArgs) Handles

    anEmployee.SalarySecurityEverrt

    End Sub

    End Module

    Обратите внимание на символ подчеркивания, добавленный VB .NET между именем переменной с ключевым словом WithEvents (anEmployee) и именем события (SalarySecurityEvent), — с ним обработчик внешне почти не отличается от процедур событий в VB6.

    Также обратите внимание на идентификацию объекта Sender полным именем (в формате пространство_имен. имя_класса). Наличие дополнительных символов подчеркивания в пространстве имен объясняется тем, что пробелы в них не разрешены, поэтому VB .NET автоматически преобразует имя решения «Event Handling 1» в «Event_Handling_l» (рис. 6.3). Наконец, ключевое слово Handles сообщает исполнительной среде, какое событие обрабатывается этой процедурой.

    Рис. 6.3. Окно свойств решения с обработкой событий

    Чтобы пример стал более интересным, вместо простого вывода в консольное окно 'мы включим в процедуру события команду вызова диалогового окна:

    Public Sub anEmployee_SalarySecurityEvent(ByVal Sender As

    Event_Handling_I.EmployeeWithEvents. ByVal e As System.EventArgs)

    Handles anEmployee.SalarySecurityEvent

    MsgBox(Sender.TheName &"had an improper salary raise attempted!")

    End Sub

    От приемника событий мы переходим к источнику. В класс Employee из главы 4 необходимо внести два изменения, выделенные в следующем листинге жирным шрифтом:

    Public Class EmployeeWithEvents

    Private m_Name As String

    Private m_Salary As Decimal

    Private Const LIMIT As Decimal =0.1D

    Public Event SalarySecurityEventCByVal Sender As

    EmployeeWithEvents,ByVal e As EventArgs)

    Public Sub NewCByVal

    aName As String. ByVal curSalary As Decimal)

    m_Name = aName

    m_Salary = curSalary

    End Sub Readonly Property TheName() As String

    Get

    Return m_Name

    End Get

    End Property Readonly Property Salary() As Decimal s,

    Get

    Return m_Salary

    End Get ' '

    End Property

    Public Overloads Sub RaiseSalary(ByVal Percent As Decimal)

    If Percent > LIMIT'Then

    'Операция запрещена - необходим пароль

    RaiseEvent SalarySecurityEventtMe, New System.EventArgs())

    Else

    m_Sa1ary = (1 + Percent) * m_Salary

    End If

    End Sub

    Public Overloads Sub RaiseSalary(ByVal Percent As Decimal.

    ByVal Password As String)

    If Password = "special" Then

    m_Salary = (1 + Percent) * m_Salary

    End If

    End Sub

    End Class

    Первый выделенный фрагмент объявляет событие, а второй инициирует его при попытке недопустимого повышения зарплаты.

    Примерный результат запуска программы показан на рис. 6.4. При нажатии кнопки ОК окно сообщения исчезает, и в консольном окне выводится строка, из которой видно, что зарплата Тома не изменилась.

    Переменные WithEvents потребляют системные ресурсы. Как только такая перемен-ная становится ненужной, присвойте ей Nothing.

    Рис. 6.4. Окно сообщения, вызываемое при обработке события