pepoluan pepoluan - 6 months ago 16
Vb.net Question

Elements of ConcurrentQueue(Of T) cannot be widened

Let's say I have this Sub:

Sub Process(InputQueue as ConcurrentQueue(Of Object))
.... some codes ....
End Sub


Now I have this

Class JobDesc
.... some codes ....
End Class

Dim MainJobQueue As New ConcurrentQueue(Of JobDesc)


Why does VB.Net complain if I try to pass
MainJobQueue
as a parameter to
Process()
?

That is, doing this:

Process(MainJobQueue)


Will result in the following error message:


Value of type 'ConcurrentQueue(Of JobDesc)' cannot be converted to 'ConcurrentQueue(Of Object)'.


Is not
JobDesc
a subclass of
Object
, hence casting
JobDesc
to
Object
will be a 'widening' instead of narrowing?

Answer

Is not JobDesc a subclass of Object, hence casting JobDesc to Object will be a 'widening' instead of narrowing?

Yes, but the problem is that ConcurrentQueue is a mutable collection.

So its type parameter does not only define what objects you can get out of it, but also what new elements it will accept.

If you could straight-up cast it to ConcurrentQueue(of Object), you could then do this:

Sub Process(InputQueue as ConcurrentQueue(Of Object))
    InputQueue.Enqueue("Whoops, this is not a JobDesc!")
End Sub

Process(MainQueue)

Dim getLatestJob = MainQueue.Dequeue() // WTF, why did it give me a String?

(Well, in theory. If you actually managed to compile this, the program would crash with InvalidCastException.)

Now, if you don't plan to add any non-JobDesc elements to the queue, you have a couple of options.

  • You can enforce the type constraint by writing Process as a generic method instead.

    Sub Process(Of SomeType)(InputQueue as ConcurrentQueue(Of SomeType))
         InputQueue.Enqueue("Whoops, this is not a SomeType!")
         // will not compile, input is not SomeType
    End Sub
    
  • Or, you can cast ConcurrentQueue to a base class or interface like IEnumerable(Of T), which does not allow adding new elements to the collection, and therefore can be safely cast to a a broader type like Object (technical term: covariant).

     Dim MainQueueReadOnly As IEnumerable(Of JobDesc) = MainQueue
    
     Sub Process(InputQueue as IEnumerable(Of Object))
         InputQueue.Enqueue(anything) 
         // will not compile as IEnumerable doesn't have an .Enqueue method
     End Sub
    

If you do, however, intend to stick some Objects in the queue, then your only option is to create a new, different collection from InputQueue, one that will accept elements of types other than JobDesc.

Dim newQueue As New ConcurrentQueue(Of Object)(MainJobQueue)