VB.NET -передача события в качестве параметра

мне нужно передать событие в качестве параметра функции. Есть ли способ сделать это?

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

в качестве примера, предположим, у меня есть combobox в моем коде под названием combobox1, и у меня есть обработчик под названием indexChangedHandler. В нескольких местах моего кода у меня есть следующие две строки:

RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler

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

Private Sub setHandler(evt As Event, hndler As eventhandler)
     RemoveHandler evt, hndler
     AddHandler evt, hndler
End Sub

Так что везде, где эти две строки кода(или подобные) происходят в моей программе, я могу просто заменить их:

setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler)

до сих пор," evt как событие " часть аргумента функция setHandler выдает ошибку.

P. S: Я задал этот вопрос на нескольких других форумах и продолжаю спрашивать, почему я хочу установить обработчик сразу после его удаления. Причина в том, что динамическое добавление обработчика событий n раз приводит к выполнению обработчика n раз при возникновении события. Чтобы избежать этого, то есть убедиться, что обработчик выполняется только один раз при возникновении события, я сначала удаляю обработчик каждый раз, когда хочу добавить обработчик динамично.

вы можете спросить, почему обработчик будет добавлен несколько раз в первую очередь... Причина в том, что я добавляю обработчик только после определенного события, скажем E1, в моей форме (я добавляю обработчик в обработчике события E1). И событие E1 может произойти несколько раз в моей форме. Если я не удаляю обработчик каждый раз перед добавлением его снова, обработчик добавляется и, таким образом, выполняется несколько раз.

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

3 ответов


конечно, вы можете передавать события вокруг... Ну вы можете пройти Action(Of EventHandler) который может делать то, что вы хотите.

если у вас есть класс, который определяет событие вот так:

Public Class ComboBox
    Public Event SelectedIndexChanged As EventHandler   
End Class

данный экземпляр ComboBox затем вы можете создать добавить и удалить действия обработчика, например:

Dim combobox1 = New ComboBox()

Dim ah As Action(Of EventHandler)
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As Action(Of EventHandler)
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h

(теперь, это VB.NET код 4.0, но вы можете сделать это в 3.5, используя AddressOf и немного больше возни.)

Итак, если у меня есть обработчик Foo:

Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs)
    Console.WriteLine("Over here with Foo!")
End Sub

и Raise метод on ComboBox теперь я могу сделать это:

ah(AddressOf Foo)
combobox1.Raise()
rh(AddressOf Foo)

здесь написано сообщение " сюда с Фу!"как и ожидалось.

Я также могу создать этот метод:

Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler))
    h(AddressOf Foo)
End Sub

Я могу передавать действия обработчика событий следующим образом:

PassActionOfEventHandler(ah)
combobox1.Raise()
PassActionOfEventHandler(rh)

который снова пишет сообщение " здесь с Foo!".

теперь одна проблема, которая может быть проблемой, заключается в том, что вы можете случайно поменять add и удалите делегаты обработчика событий в коде - ведь они одного типа. Поэтому легко просто определить строго типизированные делегаты для действий добавления и удаления, например:

Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T)
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T)

код для определения экземпляров делегатов не изменяется, за исключением типов делегатов:

Dim ah As AddHandlerDelegate(Of EventHandler)
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As RemoveHandlerDelegate(Of EventHandler)
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h

так что теперь это позволяет вам быть очень творческим. Вы можете определить функцию, которая будет принимать делегат add handler, делегат remove handler и обработчик событий, который будет подключать обработчик событий, а затем верните вам IDisposable который можно использовать позже для удаления обработчика без необходимости сохранения ссылки на обработчик событий. Это удобно для использования Using заявления.

вот подпись:

Function SubscribeToEventHandler(
    ByVal h as EventHandler,
    ByVal ah As AddHandlerDelegate(Of EventHandler),
    ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable

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

combobox1.Raise()
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh)
    combobox1.Raise()
    combobox1.Raise()
End Using
combobox1.Raise()

и это пишет сообщение " здесь с Фу!" только дважды. Первый и последний Raise вызовы находятся вне подписки на мероприятие обработчик.

наслаждайтесь!


есть много разных значений термина "событие", в зависимости от контекста. В контексте, который вы ищете, событие-это сопоставленная пара методов, которые эффективно добавляют или удаляют делегат из списка подписки. К сожалению, нет класса в VB.NET для представления адреса метода без вложенного объекта. Можно, наверное, использовать отражение чтобы получить соответствующие методы, а затем передать эти методы в подпрограмму, которая вызовет их обоих, но в ваша конкретная ситуация, вероятно, была бы глупой.

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


вы не можете передавать события вокруг. Event Это не тип, это ключевое слово, которое определяет пару методов, add и remove, используемых для изменения состояния члена события класса. В этом отношении они очень похожи на свойства (которые имеют методы get и set).

как свойства, методы add и remove могут делать все, что вы хотите. Как правило, они не будут делать ничего больше, чем поддерживать экземпляр делегата, который сам является MulticastDelegate или другими словами, список делегатов которые вызываются один за другим, если событие вызывается.

вы можете ясно видеть эту структуру в C#, где она не маскируется с AddHandler/RemoveHandler, но вы непосредственно редактируете список связанных обработчиков:myObject.Event += new Delegate(...);.

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