Multi-line Lambdas in Visual Basic – When is Enough, Enough?

Multi-line Lambdas in Visual Basic – When is Enough, Enough?

  • Comments 1

Now that I’ve been writing code for a while with the latest version of Visual Basic in Visual Studio 2010, I’m at the point where I’ve seen the fun new language features in a variety of applications and samples. One of these fun new features is multi-line and statement lambdas. I favor lambdas anytime I need to write a short code block to do work on another thread or to avoid manually creating a delegate.

For example, say I have a WPF application that listens for a change in network availability and updates its UI based on the status of the network connection. Because the notification comes in on a background thread we have to make sure we marshal any UI work onto the UI thread using the Dispatcher. In previous versions of Visual Basic we would have to write the code using delegates and the AddressOf operator:

Example #1

Public Class Window2
    Private Delegate Sub UpdateUIHandler(ByVal isNetworkAvailable As Boolean)

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        AddHandler My.Computer.Network.NetworkAvailabilityChanged, _
            AddressOf Network_NetworkAvailabilityChanged
    End Sub

    Private Sub Network_NetworkAvailabilityChanged(ByVal sender As Object, _
                                ByVal e As Devices.NetworkAvailableEventArgs)
        Try
            Dim d As UpdateUIHandler = AddressOf UpdateUI
            Dispatcher.Invoke(d, e.IsNetworkAvailable)

        Catch ex As Exception
            Debug.WriteLine(ex.ToString)
        End Try
    End Sub

    Private Sub UpdateUI(ByVal isNetworkAvailable As Boolean)
        If isNetworkAvailable Then
            Me.Style = CType(Me.Resources("Connected"), Style)
        Else
            Me.Style = CType(Me.Resources("Disconnected"), Style)
        End If
    End Sub
End Class 

This is a perfect opportunity to use a multi-line statement lambda in Visual Basic 2010 instead. When working with the Dispatcher we can avoid having to set up the delegate manually and defining a separate Sub. (Note that since we are only passing in a single boolean parameter we could have used the Action delegate instead of defining our own, but we still would have to separate the implementation from the Invoke call.) Instead you can supply the Sub as a parameter to the Invoke method directly. Notice how this makes our code more concise.

Example #2 – Use a statement lambda instead of defining a delegate

Public Class Window2

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        AddHandler My.Computer.Network.NetworkAvailabilityChanged, 
            AddressOf Network_NetworkAvailabilityChanged
    End Sub

    Private Sub Network_NetworkAvailabilityChanged(ByVal sender As Object, 
                                ByVal e As Devices.NetworkAvailableEventArgs)
        Try
            Dispatcher.Invoke(Sub()
                                  If e.IsNetworkAvailable Then
                                      Me.Style = CType(Me.Resources("Connected"), Style)
                                  Else
                                      Me.Style = CType(Me.Resources("Disconnected"), Style)
                                  End If
                              End Sub)

        Catch ex As Exception
            Debug.WriteLine(ex.ToString)
        End Try
    End Sub
End Class

We can actually go even further here and provide another statement lambda directly in the AddHandler as well. This avoids having to declare the event handler method separately. Notice the “nesting” of lambdas here:

Example #3 – Use another statement lambda to define the event hander “in-line"

Public Class Window1

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        AddHandler My.Computer.Network.NetworkAvailabilityChanged,
            Sub(sender As Object, e As Devices.NetworkAvailableEventArgs)
                Try
                    Dispatcher.Invoke(Sub()
                                          If e.IsNetworkAvailable Then
                                              Me.Style =
                                                  CType(Me.Resources("Connected"), Style)
                                          Else
                                              Me.Style =
                                                  CType(Me.Resources("Disconnected"), Style)
                                          End If
                                      End Sub)
                Catch ex As Exception
                    Debug.WriteLine(ex.ToString) 
                End Try
            End Sub
    End Sub
End Class 

At first it may seem a tad confusing because it’s new syntax but I do prefer the code in example #3 because it eliminates all the extra methods we had to write before and puts everything we need right in one place. But what if we had 3 or 5 or 10 other events to add handlers to in the constructor? What if the event handlers had lengthy code or code that called other methods? Would using lambdas help or hurt readability? When is enough, enough?

Here’s an example of some confusing uses of lambdas doing very simple tasks -- just printing out integers to the console. I got this sample right from the VB compiler test team, it compiles and runs but is this readable to you?

 Dim i = Sub()
            Const c1 = 2
            Const c3 = 3
            Const c2 = 3
            Console.WriteLine(c1)
            Dim y = Sub()
                        Console.WriteLine(c2)
                    End Sub
            Dim z = Function() Function()
                                   Return c3
                               End Function
            Dim a = Sub()
                        Dim j = Function()
                                   Const c4 = 2
                                   Return c4
                                End Function
                       Console.WriteLine(j.Invoke)
                       Console.WriteLine(z.Invoke.Invoke)
                    End Sub
             a.Invoke()
             y.Invoke()
        End Sub
Dispatcher.Invoke(i)

EWW. Thanks guys.

One of my rules of thumb with any code design is “keep it simple, stupid” (another one is “if it ain’t broke don’t fix it”). Every situation is different but I tend to try to keep methods, LINQ statements, and lambdas short (20 lines or less) and readable. So if the lambda is starting to detract from readability it’s probably better to start breaking things up, or think about a different approach.

This reminds me of a scene in one of my favorite movies of all time “L.A. Story” with Steve Martin. He’s waiting for his wife played by Marilu Henner to get ready to go out to dinner. When she’s almost ready, standing next to a mirror, she tells him that the first thing she see’s when she looks in the mirror must come off because it’s way too distracting. Mind you, she’s wearing a pretty obnoxious yellow and black dress with gigantic earrings. She whirls around to the mirror, looks, and the earrings come off!

Try doing that with your code. Close your eyes, open them, and the first thing you see will probably be the code that’s too complicated and should be refactored. ;-) Using lambdas can really clean up your code, just know when to say enough is enough.

Enjoy!

Leave a Comment
  • Please add 6 and 1 and type the answer here:
  • Post
  • Hi Beth,

    Looks really interesting! I just wish I could get deeper into it, but my trial of 2010 has finished apparently...

    I've got no idea what happened to the RSS feed though, but none of the code is nested:

    Public Class Window2      Sub New()         ' This call is required by the designer.         InitializeComponent()          ' Add any initialization after the InitializeComponent() call.         AddHandler My.Computer.Network.NetworkAvailabilityChanged,              AddressOf Network_NetworkAvailabilityChanged     End Sub      Private Sub Network_NetworkAvailabilityChanged(ByVal sender As Object,                                  ByVal e As Devices.NetworkAvailableEventArgs)         Try             Dispatcher.Invoke(Sub()                                   If e.IsNetworkAvailable Then                                       Me.Style = CType(Me.Resources("Connected"), Style)                                   Else                                       Me.Style = CType(Me.Resources("Disconnected"), Style)                                   End If                               End Sub)          Catch ex As Exception             Debug.WriteLine(ex.ToString)         End Try     End Sub End Class

    Really made it hard to read... Slap those guys that work on the Blogs site around the ears to get it fixed :D

    Keep well!

    -Logan

Page 1 of 1 (1 items)