Welcome

首页 / 软件开发 / VB.NET / 基本技术:Visual Basic 2010中的泛型协变和逆变

基本技术:Visual Basic 2010中的泛型协变和逆变2012-09-04 MSDN Binyam KelileVisual Studio 2010 有一项名为泛型协变和逆变的新功能,可以用来处理泛 型接口和委托。在 Visual Studio 2010 和 Microsoft .NET Framework 4 之前 的版本中,在子类型化方面,泛型的行为是 不变的,因此类型参数不同的泛型类 型不能相互转换。

例如,如果尝试将 List(Of Derived) 传递给接受 IEnumerable(Of Base) 的 方法,将会出现错误。 但 Visual Studio 2010 可以处理类型安全的协变和逆变 ,类型安全的协变和逆变支持泛型接口和委托类 型上的协变和逆变类型参数声明 。在本文中,我将具体讨论这一功能,并介绍如何在应用程序中使用这一 功能。

因为按钮是控件,根据基本的面向对象继承原则,您可能认为下面的代码可以 正常运行:

Dim btnCollection As IEnumerable(Of Button) = New List (Of Button) From {New Button}
Dim ctrlCollection As IEnumerable(Of Control) = btnCollection

尽管这在 Visual Studio 2008 中是不允许的,并会给出错误 “IEnumerable(Of Button) 无法转换为 IEnumerable(Of Control)” 。但作为面向对象编程人员,我们知道 Button 类型的值可以转换为控件, 因此 如前所述,根据基本继承原则,这段代码应该是可以使用的。

请看下面的例子:

Dim btnCollection As IList(Of Button) = New List(Of Button) From {New Button}
Dim ctrlCollection As IList(Of Control) = btnCollection
ctrlCollection(0) = New Label
Dim firstButton As Button = btnCollection(0)

这段代码会失败并引发 InvalidCastException,因为编程人员将 IList(Of Button) 转换为 IList (Of Control),然后向其中插入一个完全不是按钮的控件 。

Visual Studio 2010 可以识别并允许类似第一个示例的代码,但在面向 .NET Framework 4 时,仍可 以不允许类似第二个示例的代码。对于大多数用户,在大 部分时间里,程序将以预期的方式运行,不需要 进一步深究。但在本文中,我将 进一步说明这些代码如何才能正常运行以及其中的原因。

协变

第一段代码将 IEnumerable(Of Button) 视为 IEnumerable(Of Control),为 什么在 Visual Studio 2010 中这是代码安全的?第二个代码示例将 IList(Of Button) 视为 IList(Of Control),为什么这不 是安全的?

第一段代码是安全的,原因在于 IEnumerable(Of T) 是“输出” 接口,也就是说,在 IEnumerable (Of Control) 中,接口用户只是从列表中读 取控件。

第二段代码不是安全的,原因在于,IList(Of T) 是“输入输出” 接口,因此在 IList(Of Control) 中,接口用户可以读取控件,也可以写入控件 。

Visual Studio 2010 支持这种代码的新语言功能称为泛型协变。在 .NET Framework 4 中, Microsoft 重新编写了框架的以下代码行:

Interface IEnumerable(Of Out T)
...
End Interface
Interface IList(Of T)
...
End Interface

IEnumerable(Of Out T) 中的 Out 批注指示,如果 IEnumerable 中有方法使 用到 T,T 只能用在输 出位置,如函数返回的输出位置或只读属性类型的输出位 置。这样,用户可以将任意 IEnumerable(Of Derived) 转换为 IEnumerable(Of Base),而不会引发 InvalidCastException。

IList 没有批注,这是因为 IList(Of T) 是输入输出接口。这样,用户不能 将 IList(Of Derived) 转换为 IList(Of Base),反之亦然;如前所述,这样会 引发 InvalidCastException。