The problem

Just look at this code. And try to figure out what’s wrong with it. Without watching the solution. You can even compile and run it without a problem.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Module Module1
 
    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Function(result) returnString = result
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub
 
    Public Class Class1
        Public Event OnSomething(result As String)
 
        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class
 
End Module
Module Module1

    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Function(result) returnString = result
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub

    Public Class Class1
        Public Event OnSomething(result As String)

        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class

End Module

The problem is that Console.WriteLine(returnString) doesn’t write test as we would expect. And we know the event gets raised because we can easily debug that. And we know that debugging the lambda is a pain in the … so we don’t really know if that gets run. We just see that returnString never gets changed so we can expect it never to have run.

The solution

Now try this.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Module Module1
 
    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Sub(result) returnString = result
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub
 
    Public Class Class1
        Public Event OnSomething(result As String)
 
        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class
 
End Module
Module Module1

    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Sub(result) returnString = result
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub

    Public Class Class1
        Public Event OnSomething(result As String)

        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class

End Module

What changed? Not a lot. The lambda is now a Sub and no longer a Function. Apparently typesafety doesn’t include lambdas. Events are subs so your lambda should be a Sub to. If it is a function it does not throw an exception nor does it fail at compile time. It just silently ignores it, which isn’t funny.

So remember Events are Subs and not Functions.

Update

As people have said in the comments what is probably happening is this.

vb.net
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Module Module1
 
    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Function(result)
                                              Return returnString = result
                                          End Function
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub
 
    Public Class Class1
        Public Event OnSomething(result As String)
 
        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class
 
End Module
Module Module1

    Sub Main()
        Dim testClass = New Class1
        Dim returnString = ""
        AddHandler testClass.OnSomething, Function(result)
                                              Return returnString = result
                                          End Function
        testClass.Dosomething()
        Console.WriteLine(returnString)
        Console.ReadLine()
    End Sub

    Public Class Class1
        Public Event OnSomething(result As String)

        Public Sub Dosomething()
            RaiseEvent OnSomething("test")
        End Sub
    End Class

End Module

Which I understand. But this still does not solve the problem why the compiler thinks an event can be a function. Seems to me some simple typechecking should have solved that.

Update2

And yes it is legal and called delegate relaxation.

And I will let Julian Wischik explain it.

(1) A lambda written “Function(r) e” will ALWAYS assume that “e” is an expression.

And a lambda written “Sub(r) s” will ALWAYS assume that “s” is a statement.

So your hypothesis for what it’s doing is correct.

In other words: the decision for whether or not to interpret the thing as an EXPRESSION or STATEMENT is done solely by whether the keyword is “Function” or “Sub”. It is not determined by the target delegate type.

(2) You wonder why it’s legal to assign a function-lambda to a void-returning-delegate-type.

Well, that’s always legal in VB. We call it “delegate relaxation”. It is type-safe.

Here’s another example of delegate relaxation:

vb.net
1
2
3
4
Dim lambda = Function()
  Return 1
End Function
Dim a As Action = lambda   ’ creates a wrapper which simply discards the returned value
Dim lambda = Function()
  Return 1
End Function
Dim a As Action = lambda   ’ creates a wrapper which simply discards the returned value

Here’s another example of delegate relaxation:

vb.net
1
2
Dim lambda = Sub() Console.WriteLine(“hello”)
Dim a As Action(Of Integer, String) = lambda   ’ here the wrapper discards the arguments
Dim lambda = Sub() Console.WriteLine(“hello”)
Dim a As Action(Of Integer, String) = lambda   ’ here the wrapper discards the arguments

And here’s another example of that “wrapper which discards arguments”…

vb.net
1
Sub Button1Click() Handles Button1.Click
Sub Button1Click() Handles Button1.Click

– here, the Button1.Click event has a delegate type with several parameters. We’re dropping them all implicitly.


Lucian