Robin Bruegger
Robin Bruegger

Reputation: 255

Action delegate in VB.NET accepts a function lambda expression

I have a constructor that takes an Action delegate as argument:

Public Class DelegateCommand
    Public Sub New(execute As Action(Of T))
        Me.New(execute, Nothing)
    End Sub
End Command

' This works as expected
Dim executeIsCalled = False
Dim command = New DelegateCommand(Sub() executeIsCalled = True)
command.Execute(Nothing)
Assert.IsTrue(executeIsCalled) ' Pass

Action does not have a return value and the MSDN states that I must use a Sub for this purpose (MSDN Action Delegate). However this is not true as it is perfectly possible to use a function delegate:

Dim executeIsCalled = False    
Dim command = New DelegateCommand(Function() executeIsCalled = True)
command.Execute(Nothing)
Assert.IsTrue(executeIsCalled) ' Fail

This compiles fine, however the executeIsCalled = True is interpreted as the return statement, leading to the unexpected result that executeIsCalled is remains false. Interestingly, you can do the following:

Dim executeIsCalled = False
Dim command = New DelegateCommand(Function()
                                          executeIsCalled = True
                                          Return False
                                      End Function)
command.Execute(Nothing)
Assert.IsTrue(executeIsCalled) ' Pass

How can I prenvent that mistakenly a Function lambda expression is used?

Upvotes: 4

Views: 2335

Answers (1)

Waescher
Waescher

Reputation: 5737

This might not perfectly solve your needs because the compiler won't help you - but at least you will discover the errors in runtime and not wonder why any variables are not set correctly.

You could use a Delegate instead of a Action<> as constructor argument. Unfortunately, VB.NET will still allow any other devs to pass in Sub() and Function() lambdas. However, you can check the ReturnType in runtime and throw an exception if it is not Void.

Public Class DelegateCommand
    Public Sub New(execute As [Delegate])

        If (Not execute.Method.ReturnType.Equals(GetType(Void))) Then
            Throw New InvalidOperationException("Cannot use lambdas providing a return value. Use Sub() instead of Function() when using this method in VB.NET!")
        End If

        execute.DynamicInvoke()
    End Sub
End Class

Void is coming from the C#-world and is mostly unknown by VB.NET-devs. There, it is used to write methods without an return value (VB: Subs) like any other method returning a value (VB: Functions).

private void MySub() 
{
    // ...
}

private bool MyFunction()
{
    return true;
}

Upvotes: 2

Related Questions