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)
假设我有这个子:
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)