Search code examples
vb.netdeferred-execution

Deferred execution in VB.NET?


Private Sub LoadData(Of T)(ByVal query As ObjectQuery(Of T), 
    ByRef result As IEnumerable(Of T))
  If Connection.State = ConnectionState.Open Then
    result = query.ToArray
  Else
    AddHandler Connection.StateChange,
      Sub(sender As Object, e As StateChangeEventArgs)
        LoadData(query, result)
      End Sub
  End If
End Sub

In the above code I am trying to recurse the LoadData function when the connection is unavailable, I want to defer the loading to when it becomes available.

The problem is the above code leads to a compiler error, since a ByRef param cannot be used in lambda expressions.

Any idea of how to do this the right way?


Solution

  • You cannot use a ByRef parameter in a lambda because it could be pointing to a location on the stack which no longer exists once the lambda execuetes. All you have to do is use a more "permanent" storage location. You can pass in an object with a property of IEnumerable(Of T) that you can set in order to assign the result.

    A probably better option is to pass in a delegate (Action<IEnumerable<T>>) that accepts the result and performs whatever action the caller requires with the result. Here's an example in C#:

    void LoadData<T>(ObjectQuery<T> query, Action<IEnumerable<T>> action)
    {
        if (Connection.State == ConnectionState.Open)
            action(query.ToArray());
        else
        {
            // create a lambda to handle the next state change
            StateChangeEventHandler lambda = null;
            lambda = (sender, e) =>
            {
                // only perform our action on transition to Open state
                if (Connection.State == ConnectionState.Open)
                {
                    // unsubscribe when we're done
                    Connection.StateChange -= lambda;
                    action(query.ToArray());
                }
            }
            // subscribe to connection state changes
            Connection.StateChange += lambda;
        }
    }
    

    And you would invoke LoadData like this:

    LoadData(query, results => listBox.DataSource = results);
    

    Note the nuances of my implementation. For example, it does not call itself within the event handler because that will cause it to resubscribe to the event if the handler is ever called with a state other than Open. It also unsubscribes from the event once the connection opens. I'm not sure how this would translate to VB, but in C# this is a 3-step process. First you must declare a variable to hold the lambda and set its value to null. Then you create the lambda, which can now reference itself to unsubscribe. And finally you can use the lambda to subscribe to the event.