VB.NET - передача события в качестве параметра - PullRequest
10 голосов
/ 10 ноября 2010

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

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

В качестве примера, скажем, в моем коде есть поле со списком 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 as Event» аргумента функции setHandler выдает ошибку.

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

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

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

Ответы [ 3 ]

12 голосов
/ 10 ноября 2010

Конечно, вы можете передавать события ... Ну, вы можете передать 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 на 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!».

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

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

Так что теперь это позволяет вам быть очень креативным. Вы можете определить функцию, которая будет принимать делегат обработчика добавления, делегат обработчика удаления и обработчик события, который подключит обработчик события, а затем вернет вам 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 находятся вне подписки на обработчик событий.

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

3 голосов
/ 10 ноября 2010

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

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

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

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

2 голосов
/ 15 ноября 2010

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

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

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