Cамоучитель по VB.NET

         

Групповые делегаты


В приведенных выше примерах в делегате инкапсулировался адрес одной функции или процедуры.

Нередко в делегатах требуется инкапсулировать сразу несколько процедур (инкапсуляция нескольких функций особого смысла не имеет — каким должно быть возвращаемое значение?). Подобные делегаты называются групповыми (multicast) и реализуются в виде делегата, содержащего несколько однотипных делегатов. При наличии группового делегата все инкапсулированные процедуры вызываются одним методом Invoke, причем это происходит в соответствии с порядком занесения их делегатов в групповой делегат.

Чтобы создать групповой делегат, следует объединить минимум двух делегатов одного типа и присвоить результат переменной того же типа. Задача решается статическим методом Combine класса System.Delegate, который возвращает новый делегат.

Допустим, firstDel и secDel — экземпляры класса MyMultiCastDelegate. Следующая команда объединяет firstDel и secDel в групповой делегат, хранящийся в

firstDel: firstDel =System.Delegate.Combine(firstDel,secDel)

Ниже приведено простое приложение, объединяющее адреса нескольких функций в групповом делегате:

1 Option Strict On

2 Module Modulel

3 Sub Main()

4 Console.WriteLine("Calling delegate function...")

5 RegisterDelegate(AddressOf CallBackHandlerl)

6 RegisterDelegate(AddressOf CallBackHandler2)

7 Call Delegates ()

8 Console.WriteLine(

9 "Finished calling.delegate function...")

10 Console.ReadLine()

11 End Sub

12 Public Sub CallBackHandlerHByVal lngVal As RETURNJALUES)

13 Console.WriteLine("Callback 1 returned " & IngVal)

14 End Sub

15 Public Sub CallBackHandler2(ByVallngVal As RETURNJALUES)

16 Console.WriteLine("Callback 2 returned " & IngVal)

17 End Sub

18 End Module

19 Module Module2

20 Public Delegate Sub CallBackFunc(ByVallngValAs RETURN_VALUES)

21 Private m_cbFunc As CallBackFunc

22 Public Enum RETURN_VALUES

23 VALUE_SUCCESS

24 VALUE_FAILURE

25 End Enum

26 Public Sub RegisterDelegate(ByRef cbFunc As CallBackFunc)

27 m_cbFunc = CType(System.Delegate.Combine(_

28 m_cbFunc.cbFunc).CallBackFunc)

29 End Sub

30 Public Sub Call Delegates ()

31 Dim IngCounter As Long = 0

32 ' Вызвать процедуры через делегата

33 ' и вернуть признак успешного вызова

34 m_cbFunc(RETURN VALUES.VALUE_SUCCESS)

35 End Sub

36 End Module

В строках 5 и 6 вызывается процедура модуля Module2 (строки 26-28), где и происходит фактическое построение группового делегата. Это возможно благодаря тому, что делегат передается по ссылке, а не по значению. Обратите внимание на преобразование типа метода Combine к типу делегата в строке 27. Непосредственный вызов функций группового делегата происходит в строках 30-35. Всем зарегистрированным функциям передается значение перечисляемого типа RETURNJALUES . VALUE_SUCCESS. Результат выполнения программы показан на рисунке.


 

Групповые делегаты как члены классов

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

щей архитектуры, где перед дополнением группового делегата новыми функциями выполняется проверка (в данном примере — весьма тривиальная). Соответствующий фрагмент выделен жирным шрифтом:

Option Strict On

Public Class DelegateServer

Public Delegate Sub ClientCallback(ByVal IngVal As Long)

Private m_Clients As ClientCallback

' Использовать конструктор по умолчанию

Public Sub RegisterDelegate(ByVal aDelegate As

ClientCallback.ByVal dolt As Boolean)

' Обычно здесь выполняется полноценная проверка.

' В данном примере функция обратного вызова регистрируется

' лишь в том случае, если второй параметр равен

True. If dolt Then

m_Clients = CType(System.Delegate.Combine(m_ Clients.aDelegate)._

ClientCallback)

End If

End Sub

Public Sub CallClients(ByVal IngVal As Long)

m_Clients( IngVal)

End Sub

End Class

Module Modulel

Sub Main()

Dim delsrv As New DelegateServer()

delsrv.RegisterDelegate(AddressOf DelegateCallbackHandlerl.True)

' He вызывается - второй параметр равен False!

delsrv.RegisterDelegate(AddressOf DelegateCal1backHandler2.False)

' Инициировать обращение к клиентам

delsrv.CallClients(125)

Console.WriteLine("Press enter to end.")

Console.ReadLine()

End Sub

Public Sub DelegateCallbackHandlerKByValIngVal As Long)

System.Console.WriteLine("DelegateCa11backHandlerl cal1ed")

End Sub

Public Sub DelegateCallbackHandler2(ByVal IngVal As Long)

System.Console.Wri teLine("DelegateCal1backHandler2 cal1ed")

End Sub

End Module

 

Делегаты и события

Мы рассмотрели разнообразные примеры использования делегатов, однако ни один из них не имел отношения к обработке событий. Впрочем, связь между делегатами и событиями в VB .NET весьма проста. При каждом использовании сокращенного синтаксиса обработки событий, описанного в первой половине главы, VB .NET незаметно определяет класс делегата для обработки события, а команда AddressOf создает экземпляр делегата для этого обработчика. Например, следующие две строки эквивалентны (EventHandler — имя неявно определяемого делегата):

AddHandler Buttonl.Click.AddressOf Me.Buttonl_Click

AddHandler Buttonl.Click.New EventHandler(AddressOf Buttonl Click)

В сущности, каждое событие соответствует делегату следующего вида:

Public Delegate Event (sender As Object.evt As EventArgs)

Вызов RaiseEvent просто приводит к вызову Invoke для автоматически сгенерированного делегата.



Содержание раздела