Делегаты (Visual Basic)

Делегаты являются объектами, которые ссылаются на методы. Иногда их описывают как типобезопасные указатели функций, поскольку они похожи на указатели функций, используемые в других языках программирования. Но в отличие от указателей функций, Visual Basic делегаты являются ссылочным типом на основе классаSystem.Delegate. Делегаты могут ссылаться на оба вида общих методов — на методы, которые могут вызываться без определенного экземпляра класса, и на методы экземпляра.

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

Делегаты полезны в тех ситуациях, когда требуется промежуточное звено между вызывающей и вызываемой процедурами. Например, вы хотите создать объект, который создает события и в зависимости от обстоятельств может вызывать разные обработчики событий. К сожалению, этот объект не может знать заранее, какой обработчик событий обрабатывает конкретное событие. Visual Basic позволяет динамически связывать обработчики событий с событиями, создавая делегат при использовании инструкцииAddHandler. Во время выполнения делегат передает вызовы в соответствующий обработчик событий.

Хотя вы можете создавать собственные делегаты, в большинстве случаев Visual Basic создает делегат и заботится о них. Например, инструкция Event неявно определяет класс делегата с именем <EventName>EventHandler. Он размещается как вложенный класс в том классе, который содержит инструкцию Event, а его сигнатура совпадает с сигнатурой события. Инструкция AddressOf неявно создает экземпляр делегата, который ссылается на конкретную процедуру. Следующие два фрагмента кода эквивалентны. В первой строке вы видите явное создание экземпляра EventHandler, который ссылается на метод Button1_Click, переданный в качестве аргумента. Вторая строка делает то же самое, но более удобна в использовании.

AddHandler Button1.Click, New EventHandler(AddressOf Button1_Click)
' The following line of code is shorthand for the previous line.
AddHandler Button1.Click, AddressOf Me.Button1_Click

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

Объявление событий, использующих существующий тип делегата

В некоторых ситуациях вам потребуется объявить, чтобы событие использовало в качестве своего делегата уже существующий тип делегата. Для этого используется следующий синтаксис:

Delegate Sub DelegateType()
Event AnEvent As DelegateType

Это удобно, когда нужно направить к одному обработчику несколько событий.

Переменные и параметры делегата

Делегаты можно использовать для других, не связанных с событиями задач, например в свободной потоковой модели или в процедурах, которые должны вызывать разные версии функции во время выполнения.

Предположим, у вас есть рекламное приложения, которое содержит список с названиями автомобилей. Рекламные объявления сортируются по названию, которое обычно содержит модель автомобиля. Если у некоторых автомобилей перед моделью указан год производства, возникает проблема. Эта проблема связана с тем, что встроенная функция сортировки упорядочивает значения только по кодам знаков. В результате все объявления, которые начинаются с даты, располагаются первыми, и лишь затем идут объявления с моделью в начале строки.

Чтобы устранить эту проблему, можно создать в классе специальную процедуру сортировки, которая для большинства списков использует стандартную сортировку по алфавиту, но во время выполнения переключается на пользовательскую сортировку для объявлений о продаже автомобилей. Для этого во время выполнения пользовательская процедура сортировки передается классу сортировки с помощью делегатов.

AddressOf и лямбда-выражения

Каждый класс делегата определяет конструктор, которому передается спецификация метода объекта. Аргумент конструктора делегата должен быть ссылкой на метод или лямбда-выражение.

Чтобы указать ссылку на метод, используйте следующий синтаксис:

AddressOf [expression.]methodName

Тип expression во время компиляции должен представлять собой имя класса или интерфейса, который содержит метод с указанным именем, сигнатура которого соответствует сигнатуре класса делегата. methodName должен быть общим методом или методом экземпляра. methodName всегда является обязательным, даже если делегат создается для метода по умолчанию в классе.

Чтобы указать лямбда-выражение, используйте следующий синтаксис:

Function ([parm As type, parm2 As type2, ...]) expression

В следующем примере показано, как использовать AddressOf и лямбда-выражения для указания ссылки на делегат.

Module Module1

    Sub Main()
        ' Create an instance of InOrderClass and assign values to the properties.
        ' InOrderClass method ShowInOrder displays the numbers in ascending 
        ' or descending order, depending on the comparison method you specify.
        Dim inOrder As New InOrderClass
        inOrder.Num1 = 5
        inOrder.Num2 = 4

        ' Use AddressOf to send a reference to the comparison function you want
        ' to use.
        inOrder.ShowInOrder(AddressOf GreaterThan)
        inOrder.ShowInOrder(AddressOf LessThan)

        ' Use lambda expressions to do the same thing.
        inOrder.ShowInOrder(Function(m, n) m > n)
        inOrder.ShowInOrder(Function(m, n) m < n)
    End Sub

    Function GreaterThan(ByVal num1 As Integer, ByVal num2 As Integer) As Boolean
        Return num1 > num2
    End Function

    Function LessThan(ByVal num1 As Integer, ByVal num2 As Integer) As Boolean
        Return num1 < num2
    End Function

    Class InOrderClass
        ' Define the delegate function for the comparisons.
        Delegate Function CompareNumbers(ByVal num1 As Integer, ByVal num2 As Integer) As Boolean
        ' Display properties in ascending or descending order.
        Sub ShowInOrder(ByVal compare As CompareNumbers)
            If compare(_num1, _num2) Then
                Console.WriteLine(_num1 & "  " & _num2)
            Else
                Console.WriteLine(_num2 & "  " & _num1)
            End If
        End Sub

        Private _num1 As Integer
        Property Num1() As Integer
            Get
                Return _num1
            End Get
            Set(ByVal value As Integer)
                _num1 = value
            End Set
        End Property

        Private _num2 As Integer
        Property Num2() As Integer
            Get
                Return _num2
            End Get
            Set(ByVal value As Integer)
                _num2 = value
            End Set
        End Property
    End Class
End Module

Сигнатура функции должна соответствовать сигнатуре типа делегата. Дополнительные сведения о лямбда-выражениях см. в разделе "Лямбда-выражения". Дополнительные примеры лямбда-выражений и назначений AddressOf делегатам см. в статье Relaxed Delegate Conversion (Неявное преобразование делегата).

Заголовок Описание
Практическое руководство. Вызов метода делегата Предоставляет пример, в котором показано, как связать метод с делегатом и вызвать этот метод через делегат.
Практическое руководство. Передача процедур другой процедуре в Visual Basic Демонстрирует использование делегатов для передачи одной процедуры в другую процедуру.
Неявное преобразование делегата Описывает, как можно назначить делегаты или обработчики для подпрограмм и функций, если их сигнатуры не совпадают
События Предоставляет обзор событий в Visual Basic.