0

The pattern below smells for a few reasons, but for the purposes of this question, let's focus on the global variables and the event handler:

Dim objectQueue As New BlockingCollection(Of Object)
Dim objectQueueCancel As New CancellationTokenSource

Sub Main()
    Task.Run(Sub()
                 If Console.ReadKey.KeyChar() = "c" Then objectQueueCancel.Cancel()
             End Sub)
    Dim t1 As Task = Task.Run(Sub() NonBlockingConsumer())
    Dim t2 As Task = Task.Run(Sub() NonBlockingProducer())
    Task.WaitAll(t1, t2)
    objectQueueCancel.Dispose()
    Console.WriteLine("Press the Enter key to exit.")
    Console.ReadLine()
End Sub

Private Sub NonBlockingProducer()
    Dim sourceObjects As ICollection(Of Object) = Nothing 'Sanitized for StackOverflow
    For Each sourceObject In sourceObjects
        If sourceObject.IsEnabled Then
            Dim objectEventGenerator As New objectEventGenerator(sourceObject)
            AddHandler objectEventGenerator.objectEvent, AddressOf Handler_ObjectEvent
            objectEventGenerator.Enabled = True
        End If
        If objectQueue.IsAddingCompleted Then Exit For
    Next sourceObject
End Sub

Public Sub Handler_ObjectEvent(ByVal sender As Object, ByVal e As objectEventArgs)
    If Not objectQueue.IsAddingCompleted Then
        If Not e.EventException Is Nothing Then
            Environment.ExitCode = -1
        Else
            Dim itemToAdd As Object = Nothing
            Dim itemQueued As Boolean = False
            Do
                Try
                    itemQueued = objectQueue.TryAdd(itemToAdd, 0, objectQueueCancel.Token) 'TODO Tweak enqueue timeout
                Catch ex As OperationCanceledException
                    objectQueue.CompleteAdding()
                    Exit Do
                End Try
                If itemQueued Then
                    itemToAdd = Nothing
                Else
                    Thread.Sleep(0)
                End If
            Loop While Not itemQueued
        End If
    End If
End Sub

The only way of programmatically accessing the objects in the target use case is through an event listener, and while this code executes it has two pesky global variables. If I define / Dim the BlockingCollection and CancellationTokenSource in the Main() sub, I would have to pass them (I expect ByRef) into the event handler by way of NonBlockingProducer(). However, the event handler's signature is fixed, i.e. I can't simply extend it to be able to pass the variables in...

What is the "proper" way of refactoring this to eliminate the global variables?

  • Through `objectEventArgs`? – SSS Aug 15 '17 at 07:07
  • As I understand it, objectEventArgs is defined and passed to the handler by the event generator / calling method from the external class; I don't believe there is a way to append my own parameters into it. – Ryan Southwell Aug 15 '17 at 10:37
  • I guess it depends whether you have source code access to `objectEventArgs`. Is this a subclass of the standard `EventArgs` class? Many classes that raise events create a subclass of `EventArgs` to hand event parameters to the handler. – SSS Aug 16 '17 at 01:53
  • It is indeed a subclass of EventArgs, but one that I do not have access to the source for it. – Ryan Southwell Aug 16 '17 at 02:20
  • Conventional wisdom as to whether this is possible per https://stackoverflow.com/questions/23139473/how-to-use-pass-parameters-to-event-handlers is no. Is there a workaround? – Ryan Southwell Aug 16 '17 at 02:22
  • Hmm. Where do you want `objectQueue` and `objectQueueCancel` to live? As local variables in `main`? Or elsewhere? – SSS Aug 16 '17 at 03:43
  • I'd like to declare them in main, as I have been told by people wiser than me that global variables are A Bad Thing. – Ryan Southwell Aug 16 '17 at 07:45
  • In that case they will need to be passed into `NonBlockingProducer` and `NonBlockingConsumer`, then added as a property of `objectEventGenerator`. You can then `DirectCast` the `sender` parameter in the event handler to retrieve the property. If you do not have source code access to `objectEventGenerator` then I doubt you will be able to remove the global variables. – SSS Aug 16 '17 at 07:53
  • Would it be possible to extend shim the event to append to objectEventArgs without directly extending or inheriting from objectEventGenerator? – Ryan Southwell Aug 16 '17 at 17:07

0 Answers0