ConcurrentQueue(Of T) 的元素不能加宽

Elements of ConcurrentQueue(Of T) cannot be widened

假设我有这个子:

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

现在我有了这个

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

Dim MainJobQueue As New ConcurrentQueue(Of JobDesc)

如果我尝试将 MainJobQueue 作为参数传递给 Process(),为什么 VB.Net 会报错?

也就是这样做:

Process(MainJobQueue)

将导致以下错误消息:

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

不是 JobDesc Object 的子类,因此将 JobDesc 转换为 Object 将是 'widening' 而不是缩小?

你可以创建一个像这样的通用方法(如果我还记得正确的话 vb.net)

Sub Process(of T as Object)(InputQueue as ConcurrentQueue(Of T))

这没有像您预期的那样工作,因为 ConcurrentQueue 不是协变的。来自 docs:

Covariance enables you to use a more derived type than originally specified.

You can assign an instance of IEnumerable (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>.

如果您更改方法以接受 IEnumerable(Of Object)IReadOnlyList(Of Object),它会编译得很好,因为这些接口 协变的:

Sub Process(InputQueue As IEnumerable(Of Object))
    '....some codes ....
End Sub

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

是的,但问题是 ConcurrentQueue 是一个 mutable 集合。

因此它的类型参数不仅定义了您可以从中获取哪些对象,还定义了它将接受哪些元素。

如果你可以直接将它转换为 ConcurrentQueue(of Object),你可以这样做:

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?

(好吧,理论上。如果你真的设法编译这个,程序会崩溃 InvalidCastException。)

现在,如果您计划将任何非JobDesc元素添加到队列中,您有几个选择。

  • 您可以通过将 Process 编写为泛型方法来强制执行类型约束。

    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
    
  • 或者,您可以将 ConcurrentQueue 转换为基础 class 或类似 IEnumerable(Of T) 的接口,这 而不是 允许向集合中添加新元素,因此可以安全地转换为更广泛的类型,例如 Object(技术术语: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
    

如果您 ,但是,打算在队列中插入一些 Object,那么您唯一的选择是创建一个 新的,来自 InputQueue 的不同 集合,一个将接受 JobDesc 以外类型的元素的集合。

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