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?
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 Object
s 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)