Хотя наши делегаты прекрасно выполняют возложенную на них функцию, но в языке VB.NET для этих же целей предусмотрены более простые и удобные конструкции
под названием события. Итак, продолжим работу с классом Client
из прошлой главы и изменим его следующим образом:
Public Class Client Inherits Person Implements IAccount 'Объявляем делегат Public Delegate Sub AccountStateHandler(message As String) 'Событие, возникающее при выводе денег Public Event Withdrowed As AccountStateHandler 'Событие возникающее при добавление на счет Public Event Added(sum As Integer) 'Переменная для хранения суммы Dim _sum As Integer 'Переменная для хранения процента Dim _procentage As Integer Public Property Bank As String 'Текущая сумма на счете ReadOnly Property CurentSum() As Integer Implements IAccount.CurentSum Get Return _sum End Get End Property 'Метод для добавления денег на счет Sub Put(sum As Integer) Implements IAccount.Put _sum += sum RaiseEvent Added(sum) End Sub 'Метод для снятия денег со счета Sub Withdraw(sum As Integer) Implements IAccount.Withdraw If sum <= CurentSum Then _sum -= sum RaiseEvent Withdrowed("Сумма " & sum & " снята со счета") Else RaiseEvent Withdrowed("Недостаточно денег на счете") End If End Sub 'Процент начислений ReadOnly Property Procentage() As Integer Implements IAccount.Procentage Get Return _procentage End Get End Property Public Overrides Sub Display() Console.WriteLine(FirstName & " " & LastName & " has an account in bank " & Bank) End Sub Public Sub New(fName As String, lName As String, _bank As String, _sum As Integer) MyBase.New(fName, lName) Bank = _bank Me._sum = _sum End Sub End Class
Здесь мы сделали несколько изменений по сравнению с предыдущей версией класса. Во-первых, мы убрали переменную делегата и методы регистрации и отмены регистрации метода делегата. Во-вторых. мы добавили два события. События объявляются с помощью ключевого слова Event. Поскольку первое событие Withdrowed объявлено как экземпляр делегата AccountStateHandler, то для его обработки потребуется метод, принимающий строку в качестве параметра. Для второго события Added делегат не задан, поэтому мы указываем параметры в объявлении события. Поэтому метод, обрабатывающий данное событие, должен будет принимать в качестве параметра значение типа Integer. Вместо вызова делегатов мы устанавливаем вызовы событий с помощью ключевого слова RaiseEvent, передавая в события значения для параметров.
Теперь обработаем события в основной программе:
Sub Main() Dim client1 As New Client("John", "Thompson", "City Bank", 200) 'Добавляем обработчики события AddHandler client1.Withdrowed, New Client.AccountStateHandler(AddressOf Color_Message) AddHandler client1.Added, AddressOf Show_Message client1.Withdraw(100) 'Удаляем обработчик события RemoveHandler client1.Withdrowed, AddressOf Color_Message client1.Withdraw(50) client1.Put(150) Console.ReadLine() End Sub Private Sub Show_Message(sum As Integer) Console.WriteLine("На счет поступило {0} $", sum) End Sub Private Sub Color_Message(message As String) 'Устанавливаем красный цвет символов Console.ForegroundColor = ConsoleColor.Red Console.WriteLine(message) 'Сбрасываем настройки цвета Console.ResetColor() End Sub
Добавление обработчиков события происходит с помощью ключевого слова AddHandler, после которого идет имя события,
которое будет обрабатываться. Далее мы можем указать делегат, либо использовать сокращенную запись добавления обработчика события.
После ключевого слова AddressOf мы указываем метод, который и будет обрабатывать событие. Удаление обработчика события происходит подобным образом
с помощью ключевого слова RemoveHandler. После удаления обработчика данное событие не будет обрабатываться методом Color_Message
.
Существует еще один способ обработки события - с помощью ключевого слова Handles:
Module Module1 Dim WithEvents client2 As New Client("Gomer", "Simpson", "City Bank", 200) Private Sub Color_Message2(message As String) Handles client2.Withdrowed 'Устанавливаем красный цвет символов Console.ForegroundColor = ConsoleColor.Red Console.WriteLine(message) 'Сбрасываем настройки цвета Console.ResetColor() End Sub End Module
В этом случае мы объявляем переменную, у которой будем обрабатывать события, с помощью ключевого слова WithEvent, которое идет сразу после модификатора доступа. При чем так объявить переменную мы можем только на уровне модуля или класса, но не на уровне процедуры или функции. После объявления с помощью WithEvent мы можем обрабатывать событие, написав в объявлении метода после параметров ключевое слово Handles, после которого следует событие объекта. Обратите внимание. что такое объявление обработчика события доступно только для одиночного объекта, то есть мы не можем объявить целый массив следующим образом:
Dim WithEvents clients(4) As Client
В этом случае нам придется добавлять обработчики через AddHandler.
Говоря о событиях, нельзя не затронуть еще одну тему. Как правило, одним из наиболее распространенных вопросов новичков, состоит в том, а что такое
параметр e
в обработчике кнопке (
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
), да и в других событиях. Ответ на этот вопрос, как
правило, следующий: e является объектом класса EventArgs
, который содержит все данные события. Добавим и в нашу программу подобный класс:
Public Class AccountEventArgs Inherits EventArgs 'Сообщение Public ReadOnly message As String 'Сумма, на которую изменился счет Public ReadOnly sum As Integer Sub New(_message As String, _sum As Integer) message = _message sum = _sum End Sub End Class
Здесь все просто - класс наследуется от класса EventArgs. Этот класс имеет два поля: одно для выводимого сообщения, и другое для хранения
величины, на которую изменился счет. С учетом этого класса изменим класс Client
:
Public Class Client Inherits Person Implements IAccount 'Объявляем делегат Public Delegate Sub AccountStateHandler(sender As Object, e As AccountEventArgs) 'Событие, возникающее при выводе денег Public Event Withdrowed As AccountStateHandler 'Событие возникающее при добавление на счет Public Event Added(sender As Object, e As AccountEventArgs) 'Переменная для хранения суммы Dim _sum As Integer 'Переменная для хранения процента Dim _procentage As Integer Public Property Bank As String 'Текущая сумма на счете ReadOnly Property CurentSum() As Integer Implements IAccount.CurentSum Get Return _sum End Get End Property 'Метод для добавления денег на счет Sub Put(sum As Integer) Implements IAccount.Put _sum += sum RaiseEvent Added(Me, New AccountEventArgs("Деньги добавлены на счет", sum)) End Sub 'Метод для снятия денег со счета Sub Withdraw(sum As Integer) Implements IAccount.Withdraw If sum <= CurentSum Then _sum -= sum RaiseEvent Withdrowed(Me, New AccountEventArgs("Деньги сняты со счета", sum)) Else RaiseEvent Withdrowed(Me, New AccountEventArgs("Недостаточно денег на счете. Операция недействительна", sum)) End If End Sub 'Процент начислений ReadOnly Property Procentage() As Integer Implements IAccount.Procentage Get Return _procentage End Get End Property Public Overrides Sub Display() Console.WriteLine(FirstName & " " & LastName & " has an account in bank " & Bank) End Sub Public Sub New(fName As String, lName As String, _bank As String, _sum As Integer) MyBase.New(fName, lName) Bank = _bank Me._sum = _sum End Sub End Class
Вместо прежних значений теперь события будут в качестве параметров принимать объект, вызвавший событие, и объект AccountEventArgs,
хранящий информацию о событии. Поскольку событие вызываем сам объект Client, то в качестве параметра sender
мы передаем ключевое слово
Me, указывающее на текущий объект. Теперь изменим основную программу:
Sub Main() Dim client1 As New Client("John", "Thompson", "City Bank", 200) AddHandler client1.Withdrowed, New Client.AccountStateHandler(AddressOf Color_Message) AddHandler client1.Added, AddressOf Show_Message client1.Withdraw(100) client1.Withdraw(150) client1.Put(150) Console.ReadLine() End Sub Private Sub Show_Message(sender As Object, e As AccountEventArgs) Console.WriteLine(e.message) Console.WriteLine("На счет добавлено {0} $", e.sum) End Sub Private Sub Color_Message(sender As Object, e As AccountEventArgs) 'Устанавливаем красный цвет символов Console.ForegroundColor = ConsoleColor.Red Console.WriteLine("Была проведена попытка снять со счета {0} $", e.sum) Console.WriteLine(e.message) 'Сбрасываем настройки цвета Console.ResetColor() End Sub
Теперь если мы запустим программу. мы получим следующий вывод:
Была проведена попытка снять со счета 100 $ Деньги сняты со счета Была проведена попытка снять со счета 150 $ Недостаточно денег на счете. Операция недействительна Деньги добавлены на счет На счет добавлено 150 $