1

I have an event in a generic class

Public class SomeClass
    Public Event ChangeEvent(oldValue As T, newValue As T)

    ' some code..
End Class

And I have a complicated method to which I pass an Action(Of T) which should get added to the event and later removed again. It would look something like the following:

Public Sub SomeSub(listener As Action(Of T, T)
    AddHandler ChangeEvent, listener
    ' some code
    RemoveHandler ChangeEvent, listener
End Sub

But on both lines the compiler gives me the following error:

Value of type 'Action(Of T, T)' cannot be converted to 'ChangeEventHandler'.

The following works, but I can't remove the handler as it was a lambda expression.

Public Sub SomeSub(listener As Action(Of T, T)
    AddHandler ChangeEvent, Sub(x, y) listener(x, y)
End Sub

Is there a solution which doesn't involve me storing the lambda as a member? Please note that I cannot change the Event. I am only in control of the Method adding the listener.

Neuron
  • 5,141
  • 5
  • 38
  • 59

2 Answers2

1

It is better if you explicitly declare your event handler delegate, rather than letting Visual Basic do it implicitly:

Public Class SomeClass(Of T)
    Public Delegate Sub ChangeEventHandler(oldValue As T, newValue As T)
    Public Event ChangeEvent As ChangeEventHandler

    ' some code..
End Class

That way you can specify the delegate as a parameter of your method, thus giving the compiler the correct signature and you the ability to both add and remove it:

Public Sub SomeSub(listener As ChangeEventHandler)
    AddHandler ChangeEvent, listener
    ' some code...
    RemoveHandler ChangeEvent, listener
End Sub

Now you and the compiler are both happy! :)

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • thanks! that did the trick. i guess i need to read more about what happens in the background with delegates and eventhandlers.. – Neuron Oct 23 '17 at 17:12
  • @LonelyNeuron : Glad I could help! What you were experiencing was merely VB.NET's syntactic sugar. Your event declaration does exactly the same as what I did above, only the compiler declares the delegate for you. – Visual Vincent Oct 23 '17 at 19:04
  • @LonelyNeuron : When subscribing to an event you're required to specify an object that is, or can be converted to an instance of the event's specific event handler. An `Action(Of T, T)(T, T)` is a completely different type than `ChangeEventHandler(T, T)`, which is what the compiler complained about. A lambda expression however is implicitly converted into the required delegate (assuming its parameters are correct) which is why it worked using one. – Visual Vincent Oct 23 '17 at 19:09
  • that makes a lot more sense now! – Neuron Oct 23 '17 at 20:28
0

It actually works like this:

    Dim handler As EventHandler

    handler = New EventHandler(Sub(s, e)
                                    RemoveHandler anyClass.AnyEvent, handler

                                    ' do anything once here ...
                                End Sub)

    AddHandler anyClass.AnyEvent, shownHandler

This will add the handler and remove it as soon as the event is executed. So the code inside the lambda will only be executed once.

The trick is to have an handler declared separately because in the lambda, you need to refer to the variable to remove the handler.

You don't need to keep any member variables at all, the runtime will store the encapsulated variables for you.


However, you have to be sure that the event is really fired (at least once). If not, you might cause a memory leak because the event handler is never removed.

Waescher
  • 5,361
  • 3
  • 34
  • 51